@blockrun/clawrouter 0.8.16 → 0.8.17
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 +22 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.js +22 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/proxy.ts","../src/x402.ts","../src/payment-cache.ts","../src/router/rules.ts","../src/router/selector.ts","../src/router/config.ts","../src/router/index.ts","../src/models.ts","../src/logger.ts","../src/stats.ts","../src/dedup.ts","../src/balance.ts","../src/errors.ts","../src/version.ts","../src/session.ts","../src/auth.ts","../src/cli.ts"],"sourcesContent":["/**\n * Local x402 Proxy Server\n *\n * Sits between OpenClaw's pi-ai (which makes standard OpenAI-format requests)\n * and BlockRun's API (which requires x402 micropayments).\n *\n * Flow:\n * pi-ai → http://localhost:{port}/v1/chat/completions\n * → proxy forwards to https://blockrun.ai/api/v1/chat/completions\n * → gets 402 → @x402/fetch signs payment → retries\n * → streams response back to pi-ai\n *\n * Optimizations (v0.3.0):\n * - SSE heartbeat: for streaming requests, sends headers + heartbeat immediately\n * before the x402 flow, preventing OpenClaw's 10-15s timeout from firing.\n * - Response dedup: hashes request bodies and caches responses for 30s,\n * preventing double-charging when OpenClaw retries after timeout.\n * - Payment cache: after first 402, pre-signs subsequent requests to skip\n * the 402 round trip (~200ms savings per request).\n * - Smart routing: when model is \"blockrun/auto\", classify query and pick cheapest model.\n * - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { finished } from \"node:stream\";\nimport type { AddressInfo } from \"node:net\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { createPaymentFetch, type PreAuthParams } from \"./x402.js\";\nimport {\n route,\n getFallbackChain,\n getFallbackChainFiltered,\n calculateModelCost,\n DEFAULT_ROUTING_CONFIG,\n type RouterOptions,\n type RoutingDecision,\n type RoutingConfig,\n type ModelPricing,\n} from \"./router/index.js\";\nimport {\n BLOCKRUN_MODELS,\n resolveModelAlias,\n getModelContextWindow,\n isReasoningModel,\n} from \"./models.js\";\nimport { logUsage, type UsageEntry } from \"./logger.js\";\nimport { getStats } from \"./stats.js\";\nimport { RequestDeduplicator } from \"./dedup.js\";\nimport { BalanceMonitor } from \"./balance.js\";\n// Error classes available for programmatic use but not used in proxy\n// (universal free fallback means we don't throw balance errors anymore)\n// import { InsufficientFundsError, EmptyWalletError } from \"./errors.js\";\nimport { USER_AGENT } from \"./version.js\";\nimport { SessionStore, getSessionId, type SessionConfig } from \"./session.js\";\n\nconst BLOCKRUN_API = \"https://blockrun.ai/api\";\nconst AUTO_MODEL = \"blockrun/auto\";\nconst AUTO_MODEL_SHORT = \"auto\"; // OpenClaw strips provider prefix\nconst FREE_MODEL = \"nvidia/gpt-oss-120b\"; // Free model for empty wallet fallback\nconst HEARTBEAT_INTERVAL_MS = 2_000;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 180_000; // 3 minutes (allows for on-chain tx + LLM response)\nconst DEFAULT_PORT = 8402;\nconst MAX_FALLBACK_ATTEMPTS = 3; // Maximum models to try in fallback chain\nconst HEALTH_CHECK_TIMEOUT_MS = 2_000; // Timeout for checking existing proxy\nconst RATE_LIMIT_COOLDOWN_MS = 60_000; // 60 seconds cooldown for rate-limited models\nconst PORT_RETRY_ATTEMPTS = 5; // Max attempts to bind port (handles TIME_WAIT)\nconst PORT_RETRY_DELAY_MS = 1_000; // Delay between retry attempts\n\n/**\n * Transform upstream payment errors into user-friendly messages.\n * Parses the raw x402 error and formats it nicely.\n */\nfunction transformPaymentError(errorBody: string): string {\n try {\n // Try to parse the error JSON\n const parsed = JSON.parse(errorBody) as {\n error?: string;\n details?: string;\n };\n\n // Check if this is a payment verification error\n if (parsed.error === \"Payment verification failed\" && parsed.details) {\n // Extract the nested JSON from details\n // Format: \"Verification failed: {json}\\n\"\n const match = parsed.details.match(/Verification failed:\\s*(\\{.*\\})/s);\n if (match) {\n const innerJson = JSON.parse(match[1]) as {\n invalidMessage?: string;\n invalidReason?: string;\n payer?: string;\n };\n\n if (innerJson.invalidReason === \"insufficient_funds\" && innerJson.invalidMessage) {\n // Parse \"insufficient balance: 251 < 11463\"\n const balanceMatch = innerJson.invalidMessage.match(\n /insufficient balance:\\s*(\\d+)\\s*<\\s*(\\d+)/i,\n );\n if (balanceMatch) {\n const currentMicros = parseInt(balanceMatch[1], 10);\n const requiredMicros = parseInt(balanceMatch[2], 10);\n const currentUSD = (currentMicros / 1_000_000).toFixed(6);\n const requiredUSD = (requiredMicros / 1_000_000).toFixed(6);\n const wallet = innerJson.payer || \"unknown\";\n const shortWallet =\n wallet.length > 12 ? `${wallet.slice(0, 6)}...${wallet.slice(-4)}` : wallet;\n\n return JSON.stringify({\n error: {\n message: `Insufficient USDC balance. Current: $${currentUSD}, Required: ~$${requiredUSD}`,\n type: \"insufficient_funds\",\n wallet: wallet,\n current_balance_usd: currentUSD,\n required_usd: requiredUSD,\n help: `Fund wallet ${shortWallet} with USDC on Base, or use free model: /model free`,\n },\n });\n }\n }\n }\n }\n } catch {\n // If parsing fails, return original\n }\n return errorBody;\n}\n\n/**\n * Track rate-limited models to avoid hitting them again.\n * Maps model ID to the timestamp when the rate limit was hit.\n */\nconst rateLimitedModels = new Map<string, number>();\n\n/**\n * Check if a model is currently rate-limited (in cooldown period).\n */\nfunction isRateLimited(modelId: string): boolean {\n const hitTime = rateLimitedModels.get(modelId);\n if (!hitTime) return false;\n\n const elapsed = Date.now() - hitTime;\n if (elapsed >= RATE_LIMIT_COOLDOWN_MS) {\n rateLimitedModels.delete(modelId);\n return false;\n }\n return true;\n}\n\n/**\n * Mark a model as rate-limited.\n */\nfunction markRateLimited(modelId: string): void {\n rateLimitedModels.set(modelId, Date.now());\n console.log(`[ClawRouter] Model ${modelId} rate-limited, will deprioritize for 60s`);\n}\n\n/**\n * Reorder models to put rate-limited ones at the end.\n */\nfunction prioritizeNonRateLimited(models: string[]): string[] {\n const available: string[] = [];\n const rateLimited: string[] = [];\n\n for (const model of models) {\n if (isRateLimited(model)) {\n rateLimited.push(model);\n } else {\n available.push(model);\n }\n }\n\n return [...available, ...rateLimited];\n}\n\n/**\n * Check if response socket is writable (prevents write-after-close errors).\n * Returns true only if all conditions are safe for writing.\n */\nfunction canWrite(res: ServerResponse): boolean {\n return (\n !res.writableEnded &&\n !res.destroyed &&\n res.socket !== null &&\n !res.socket.destroyed &&\n res.socket.writable\n );\n}\n\n/**\n * Safe write with backpressure handling.\n * Returns true if write succeeded, false if socket is closed or write failed.\n */\nfunction safeWrite(res: ServerResponse, data: string | Buffer): boolean {\n if (!canWrite(res)) {\n return false;\n }\n return res.write(data);\n}\n\n// Extra buffer for balance check (on top of estimateAmount's 20% buffer)\n// Total effective buffer: 1.2 * 1.5 = 1.8x (80% safety margin)\n// This prevents x402 payment failures after streaming headers are sent,\n// which would trigger OpenClaw's 5-24 hour billing cooldown.\nconst BALANCE_CHECK_BUFFER = 1.5;\n\n/**\n * Get the proxy port from environment variable or default.\n */\nexport function getProxyPort(): number {\n const envPort = process.env.BLOCKRUN_PROXY_PORT;\n if (envPort) {\n const parsed = parseInt(envPort, 10);\n if (!isNaN(parsed) && parsed > 0 && parsed < 65536) {\n return parsed;\n }\n }\n return DEFAULT_PORT;\n}\n\n/**\n * Check if a proxy is already running on the given port.\n * Returns the wallet address if running, undefined otherwise.\n */\nasync function checkExistingProxy(port: number): Promise<string | undefined> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);\n\n try {\n const response = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n\n if (response.ok) {\n const data = (await response.json()) as { status?: string; wallet?: string };\n if (data.status === \"ok\" && data.wallet) {\n return data.wallet;\n }\n }\n return undefined;\n } catch {\n clearTimeout(timeoutId);\n return undefined;\n }\n}\n\n/**\n * Error patterns that indicate a provider-side issue (not user's fault).\n * These errors should trigger fallback to the next model in the chain.\n */\nconst PROVIDER_ERROR_PATTERNS = [\n /billing/i,\n /insufficient.*balance/i,\n /credits/i,\n /quota.*exceeded/i,\n /rate.*limit/i,\n /model.*unavailable/i,\n /model.*not.*available/i,\n /service.*unavailable/i,\n /capacity/i,\n /overloaded/i,\n /temporarily.*unavailable/i,\n /api.*key.*invalid/i,\n /authentication.*failed/i,\n];\n\n/**\n * HTTP status codes that indicate provider issues worth retrying with fallback.\n */\nconst FALLBACK_STATUS_CODES = [\n 400, // Bad request - sometimes used for billing errors\n 401, // Unauthorized - provider API key issues\n 402, // Payment required - but from upstream, not x402\n 403, // Forbidden - provider restrictions\n 429, // Rate limited\n 500, // Internal server error\n 502, // Bad gateway\n 503, // Service unavailable\n 504, // Gateway timeout\n];\n\n/**\n * Check if an error response indicates a provider issue that should trigger fallback.\n */\nfunction isProviderError(status: number, body: string): boolean {\n // Check status code first\n if (!FALLBACK_STATUS_CODES.includes(status)) {\n return false;\n }\n\n // For 5xx errors, always fallback\n if (status >= 500) {\n return true;\n }\n\n // For 4xx errors, check the body for known provider error patterns\n return PROVIDER_ERROR_PATTERNS.some((pattern) => pattern.test(body));\n}\n\n/**\n * Valid message roles for OpenAI-compatible APIs.\n * Some clients send non-standard roles (e.g., \"developer\" instead of \"system\").\n */\nconst VALID_ROLES = new Set([\"system\", \"user\", \"assistant\", \"tool\", \"function\"]);\n\n/**\n * Role mappings for non-standard roles.\n * Maps client-specific roles to standard OpenAI roles.\n */\nconst ROLE_MAPPINGS: Record<string, string> = {\n developer: \"system\", // OpenAI's newer API uses \"developer\" for system messages\n model: \"assistant\", // Some APIs use \"model\" instead of \"assistant\"\n};\n\ntype ChatMessage = { role: string; content: string | unknown };\n\n/**\n * Anthropic tool ID pattern: only alphanumeric, underscore, and hyphen allowed.\n * Error: \"messages.X.content.Y.tool_use.id: String should match pattern '^[a-zA-Z0-9_-]+$'\"\n */\nconst VALID_TOOL_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;\n\n/**\n * Sanitize a tool ID to match Anthropic's required pattern.\n * Replaces invalid characters with underscores.\n */\nfunction sanitizeToolId(id: string | undefined): string | undefined {\n if (!id || typeof id !== \"string\") return id;\n if (VALID_TOOL_ID_PATTERN.test(id)) return id;\n\n // Replace invalid characters with underscores\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\n/**\n * Type for messages with tool calls (OpenAI format).\n */\ntype MessageWithTools = ChatMessage & {\n tool_calls?: Array<{ id?: string; type?: string; function?: unknown }>;\n tool_call_id?: string;\n};\n\n/**\n * Type for content blocks that may contain tool IDs (Anthropic format in OpenAI wrapper).\n */\ntype ContentBlock = {\n type?: string;\n id?: string;\n tool_use_id?: string;\n [key: string]: unknown;\n};\n\n/**\n * Sanitize all tool IDs in messages to match Anthropic's pattern.\n * Handles both OpenAI format (tool_calls, tool_call_id) and content block formats.\n */\nfunction sanitizeToolIds(messages: ChatMessage[]): ChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n let hasChanges = false;\n const sanitized = messages.map((msg) => {\n const typedMsg = msg as MessageWithTools;\n let msgChanged = false;\n let newMsg = { ...msg } as MessageWithTools;\n\n // Sanitize tool_calls[].id in assistant messages\n if (typedMsg.tool_calls && Array.isArray(typedMsg.tool_calls)) {\n const newToolCalls = typedMsg.tool_calls.map((tc) => {\n if (tc.id && typeof tc.id === \"string\") {\n const sanitized = sanitizeToolId(tc.id);\n if (sanitized !== tc.id) {\n msgChanged = true;\n return { ...tc, id: sanitized };\n }\n }\n return tc;\n });\n if (msgChanged) {\n newMsg = { ...newMsg, tool_calls: newToolCalls };\n }\n }\n\n // Sanitize tool_call_id in tool messages\n if (typedMsg.tool_call_id && typeof typedMsg.tool_call_id === \"string\") {\n const sanitized = sanitizeToolId(typedMsg.tool_call_id);\n if (sanitized !== typedMsg.tool_call_id) {\n msgChanged = true;\n newMsg = { ...newMsg, tool_call_id: sanitized };\n }\n }\n\n // Sanitize content blocks if content is an array (Anthropic-style content)\n if (Array.isArray(typedMsg.content)) {\n const newContent = (typedMsg.content as ContentBlock[]).map((block) => {\n if (!block || typeof block !== \"object\") return block;\n\n let blockChanged = false;\n let newBlock = { ...block };\n\n // tool_use blocks have \"id\"\n if (block.type === \"tool_use\" && block.id && typeof block.id === \"string\") {\n const sanitized = sanitizeToolId(block.id);\n if (sanitized !== block.id) {\n blockChanged = true;\n newBlock = { ...newBlock, id: sanitized };\n }\n }\n\n // tool_result blocks have \"tool_use_id\"\n if (\n block.type === \"tool_result\" &&\n block.tool_use_id &&\n typeof block.tool_use_id === \"string\"\n ) {\n const sanitized = sanitizeToolId(block.tool_use_id);\n if (sanitized !== block.tool_use_id) {\n blockChanged = true;\n newBlock = { ...newBlock, tool_use_id: sanitized };\n }\n }\n\n if (blockChanged) {\n msgChanged = true;\n return newBlock;\n }\n return block;\n });\n\n if (msgChanged) {\n newMsg = { ...newMsg, content: newContent };\n }\n }\n\n if (msgChanged) {\n hasChanges = true;\n return newMsg;\n }\n return msg;\n });\n\n return hasChanges ? sanitized : messages;\n}\n\n/**\n * Normalize message roles to standard OpenAI format.\n * Converts non-standard roles (e.g., \"developer\") to valid ones.\n */\nfunction normalizeMessageRoles(messages: ChatMessage[]): ChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n let hasChanges = false;\n const normalized = messages.map((msg) => {\n if (VALID_ROLES.has(msg.role)) return msg;\n\n const mappedRole = ROLE_MAPPINGS[msg.role];\n if (mappedRole) {\n hasChanges = true;\n return { ...msg, role: mappedRole };\n }\n\n // Unknown role - default to \"user\" to avoid API errors\n hasChanges = true;\n return { ...msg, role: \"user\" };\n });\n\n return hasChanges ? normalized : messages;\n}\n\n/**\n * Normalize messages for Google models.\n * Google's Gemini API requires the first non-system message to be from \"user\".\n * If conversation starts with \"assistant\"/\"model\", prepend a placeholder user message.\n */\n\nfunction normalizeMessagesForGoogle(messages: ChatMessage[]): ChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n // Find first non-system message\n let firstNonSystemIdx = -1;\n for (let i = 0; i < messages.length; i++) {\n if (messages[i].role !== \"system\") {\n firstNonSystemIdx = i;\n break;\n }\n }\n\n // If no non-system messages, return as-is\n if (firstNonSystemIdx === -1) return messages;\n\n const firstRole = messages[firstNonSystemIdx].role;\n\n // If first non-system message is already \"user\", no change needed\n if (firstRole === \"user\") return messages;\n\n // If first non-system message is \"assistant\" or \"model\", prepend a user message\n if (firstRole === \"assistant\" || firstRole === \"model\") {\n const normalized = [...messages];\n normalized.splice(firstNonSystemIdx, 0, {\n role: \"user\",\n content: \"(continuing conversation)\",\n });\n return normalized;\n }\n\n return messages;\n}\n\n/**\n * Check if a model is a Google model that requires message normalization.\n */\nfunction isGoogleModel(modelId: string): boolean {\n return modelId.startsWith(\"google/\") || modelId.startsWith(\"gemini\");\n}\n\n/**\n * Extended message type for thinking-enabled conversations.\n */\ntype ExtendedChatMessage = ChatMessage & {\n tool_calls?: unknown[];\n reasoning_content?: unknown;\n};\n\n/**\n * Normalize messages for thinking-enabled requests.\n * When thinking/extended_thinking is enabled, assistant messages with tool_calls\n * must have reasoning_content (can be empty string if not present).\n * Error: \"400 thinking is enabled but reasoning_content is missing in assistant tool call message\"\n */\nfunction normalizeMessagesForThinking(messages: ExtendedChatMessage[]): ExtendedChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n let hasChanges = false;\n const normalized = messages.map((msg) => {\n // Skip if not assistant or already has reasoning_content\n if (msg.role !== \"assistant\" || msg.reasoning_content !== undefined) {\n return msg;\n }\n\n // Check for OpenAI format: tool_calls array\n const hasOpenAIToolCalls =\n msg.tool_calls && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;\n\n // Check for Anthropic format: content array with tool_use blocks\n const hasAnthropicToolUse =\n Array.isArray(msg.content) &&\n (msg.content as Array<{ type?: string }>).some((block) => block?.type === \"tool_use\");\n\n if (hasOpenAIToolCalls || hasAnthropicToolUse) {\n hasChanges = true;\n return { ...msg, reasoning_content: \"\" };\n }\n return msg;\n });\n\n return hasChanges ? normalized : messages;\n}\n\n// Kimi/Moonshot models use special Unicode tokens for thinking boundaries.\n// Pattern: <|begin▁of▁thinking|>content<|end▁of▁thinking|>\n// The | is fullwidth vertical bar (U+FF5C), ▁ is lower one-eighth block (U+2581).\n\n// Match full Kimi thinking blocks: <|begin...|>content<|end...|>\nconst KIMI_BLOCK_RE = /<[||][^<>]*begin[^<>]*[||]>[\\s\\S]*?<[||][^<>]*end[^<>]*[||]>/gi;\n\n// Match standalone Kimi tokens like <|end▁of▁thinking|>\nconst KIMI_TOKEN_RE = /<[||][^<>]*[||]>/g;\n\n// Standard thinking tags that may leak through from various models\nconst THINKING_TAG_RE = /<\\s*\\/?\\s*(?:think(?:ing)?|thought|antthinking)\\b[^>]*>/gi;\n\n// Full thinking blocks: <think>content</think>\nconst THINKING_BLOCK_RE =\n /<\\s*(?:think(?:ing)?|thought|antthinking)\\b[^>]*>[\\s\\S]*?<\\s*\\/\\s*(?:think(?:ing)?|thought|antthinking)\\s*>/gi;\n\n/**\n * Strip thinking tokens and blocks from model response content.\n * Handles both Kimi-style Unicode tokens and standard XML-style tags.\n *\n * NOTE: DSML tags (<|DSML|...>) are NOT stripped - those are tool calls\n * that should be handled by the API, not hidden from users.\n */\nfunction stripThinkingTokens(content: string): string {\n if (!content) return content;\n // Strip full Kimi thinking blocks first (begin...end with content)\n let cleaned = content.replace(KIMI_BLOCK_RE, \"\");\n // Strip remaining standalone Kimi tokens\n cleaned = cleaned.replace(KIMI_TOKEN_RE, \"\");\n // Strip full thinking blocks (<think>...</think>)\n cleaned = cleaned.replace(THINKING_BLOCK_RE, \"\");\n // Strip remaining standalone thinking tags\n cleaned = cleaned.replace(THINKING_TAG_RE, \"\");\n return cleaned;\n}\n\n/** Callback info for low balance warning */\nexport type LowBalanceInfo = {\n balanceUSD: string;\n walletAddress: string;\n};\n\n/** Callback info for insufficient funds error */\nexport type InsufficientFundsInfo = {\n balanceUSD: string;\n requiredUSD: string;\n walletAddress: string;\n};\n\nexport type ProxyOptions = {\n walletKey: string;\n apiBase?: string;\n /** Port to listen on (default: 8402) */\n port?: number;\n routingConfig?: Partial<RoutingConfig>;\n /** Request timeout in ms (default: 180000 = 3 minutes). Covers on-chain tx + LLM response. */\n requestTimeoutMs?: number;\n /** Skip balance checks (for testing only). Default: false */\n skipBalanceCheck?: boolean;\n /**\n * Session persistence config. When enabled, maintains model selection\n * across requests within a session to prevent mid-task model switching.\n */\n sessionConfig?: Partial<SessionConfig>;\n onReady?: (port: number) => void;\n onError?: (error: Error) => void;\n onPayment?: (info: { model: string; amount: string; network: string }) => void;\n onRouted?: (decision: RoutingDecision) => void;\n /** Called when balance drops below $1.00 (warning, request still proceeds) */\n onLowBalance?: (info: LowBalanceInfo) => void;\n /** Called when balance is insufficient for a request (request fails) */\n onInsufficientFunds?: (info: InsufficientFundsInfo) => void;\n};\n\nexport type ProxyHandle = {\n port: number;\n baseUrl: string;\n walletAddress: string;\n balanceMonitor: BalanceMonitor;\n close: () => Promise<void>;\n};\n\n/**\n * Build model pricing map from BLOCKRUN_MODELS.\n */\nfunction buildModelPricing(): Map<string, ModelPricing> {\n const map = new Map<string, ModelPricing>();\n for (const m of BLOCKRUN_MODELS) {\n if (m.id === AUTO_MODEL) continue; // skip meta-model\n map.set(m.id, { inputPrice: m.inputPrice, outputPrice: m.outputPrice });\n }\n return map;\n}\n\n/**\n * Merge partial routing config overrides with defaults.\n */\nfunction mergeRoutingConfig(overrides?: Partial<RoutingConfig>): RoutingConfig {\n if (!overrides) return DEFAULT_ROUTING_CONFIG;\n return {\n ...DEFAULT_ROUTING_CONFIG,\n ...overrides,\n classifier: { ...DEFAULT_ROUTING_CONFIG.classifier, ...overrides.classifier },\n scoring: { ...DEFAULT_ROUTING_CONFIG.scoring, ...overrides.scoring },\n tiers: { ...DEFAULT_ROUTING_CONFIG.tiers, ...overrides.tiers },\n overrides: { ...DEFAULT_ROUTING_CONFIG.overrides, ...overrides.overrides },\n };\n}\n\n/**\n * Estimate USDC cost for a request based on model pricing.\n * Returns amount string in USDC smallest unit (6 decimals) or undefined if unknown.\n */\nfunction estimateAmount(\n modelId: string,\n bodyLength: number,\n maxTokens: number,\n): string | undefined {\n const model = BLOCKRUN_MODELS.find((m) => m.id === modelId);\n if (!model) return undefined;\n\n // Rough estimate: ~4 chars per token for input\n const estimatedInputTokens = Math.ceil(bodyLength / 4);\n const estimatedOutputTokens = maxTokens || model.maxOutput || 4096;\n\n const costUsd =\n (estimatedInputTokens / 1_000_000) * model.inputPrice +\n (estimatedOutputTokens / 1_000_000) * model.outputPrice;\n\n // Convert to USDC 6-decimal integer, add 20% buffer for estimation error\n // Minimum 100 ($0.0001) to avoid zero-amount rejections\n const amountMicros = Math.max(100, Math.ceil(costUsd * 1.2 * 1_000_000));\n return amountMicros.toString();\n}\n\n/**\n * Start the local x402 proxy server.\n *\n * If a proxy is already running on the target port, reuses it instead of failing.\n * Port can be configured via BLOCKRUN_PROXY_PORT environment variable.\n *\n * Returns a handle with the assigned port, base URL, and a close function.\n */\nexport async function startProxy(options: ProxyOptions): Promise<ProxyHandle> {\n const apiBase = options.apiBase ?? BLOCKRUN_API;\n\n // Determine port: options.port > env var > default\n const listenPort = options.port ?? getProxyPort();\n\n // Check if a proxy is already running on this port\n const existingWallet = await checkExistingProxy(listenPort);\n if (existingWallet) {\n // Proxy already running — reuse it instead of failing with EADDRINUSE\n const account = privateKeyToAccount(options.walletKey as `0x${string}`);\n const balanceMonitor = new BalanceMonitor(account.address);\n const baseUrl = `http://127.0.0.1:${listenPort}`;\n\n // Verify the existing proxy is using the same wallet (or warn if different)\n if (existingWallet !== account.address) {\n console.warn(\n `[ClawRouter] Existing proxy on port ${listenPort} uses wallet ${existingWallet}, but current config uses ${account.address}. Reusing existing proxy.`,\n );\n }\n\n options.onReady?.(listenPort);\n\n return {\n port: listenPort,\n baseUrl,\n walletAddress: existingWallet,\n balanceMonitor,\n close: async () => {\n // No-op: we didn't start this proxy, so we shouldn't close it\n },\n };\n }\n\n // Create x402 payment-enabled fetch from wallet private key\n const account = privateKeyToAccount(options.walletKey as `0x${string}`);\n const { fetch: payFetch } = createPaymentFetch(options.walletKey as `0x${string}`);\n\n // Create balance monitor for pre-request checks\n const balanceMonitor = new BalanceMonitor(account.address);\n\n // Build router options (100% local — no external API calls for routing)\n const routingConfig = mergeRoutingConfig(options.routingConfig);\n const modelPricing = buildModelPricing();\n const routerOpts: RouterOptions = {\n config: routingConfig,\n modelPricing,\n };\n\n // Request deduplicator (shared across all requests)\n const deduplicator = new RequestDeduplicator();\n\n // Session store for model persistence (prevents mid-task model switching)\n const sessionStore = new SessionStore(options.sessionConfig);\n\n // Track active connections for graceful cleanup\n const connections = new Set<import(\"net\").Socket>();\n\n const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Add stream error handlers to prevent server crashes\n req.on(\"error\", (err) => {\n console.error(`[ClawRouter] Request stream error: ${err.message}`);\n // Don't throw - just log and let request handler deal with it\n });\n\n res.on(\"error\", (err) => {\n console.error(`[ClawRouter] Response stream error: ${err.message}`);\n // Don't try to write to failed socket - just log\n });\n\n // Finished wrapper for guaranteed cleanup on response completion/error\n finished(res, (err) => {\n if (err && err.code !== \"ERR_STREAM_DESTROYED\") {\n console.error(`[ClawRouter] Response finished with error: ${err.message}`);\n }\n // Note: heartbeatInterval cleanup happens in res.on(\"close\") handler\n // Note: completed and dedup cleanup happens in the res.on(\"close\") handler below\n });\n\n // Request finished wrapper for complete stream lifecycle tracking\n finished(req, (err) => {\n if (err && err.code !== \"ERR_STREAM_DESTROYED\") {\n console.error(`[ClawRouter] Request finished with error: ${err.message}`);\n }\n });\n\n // Health check with optional balance info\n if (req.url === \"/health\" || req.url?.startsWith(\"/health?\")) {\n const url = new URL(req.url, \"http://localhost\");\n const full = url.searchParams.get(\"full\") === \"true\";\n\n const response: Record<string, unknown> = {\n status: \"ok\",\n wallet: account.address,\n };\n\n if (full) {\n try {\n const balanceInfo = await balanceMonitor.checkBalance();\n response.balance = balanceInfo.balanceUSD;\n response.isLow = balanceInfo.isLow;\n response.isEmpty = balanceInfo.isEmpty;\n } catch {\n response.balanceError = \"Could not fetch balance\";\n }\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(response));\n return;\n }\n\n // Stats API endpoint - returns JSON for programmatic access\n if (req.url === \"/stats\" || req.url?.startsWith(\"/stats?\")) {\n try {\n const url = new URL(req.url, \"http://localhost\");\n const days = parseInt(url.searchParams.get(\"days\") || \"7\", 10);\n const stats = await getStats(Math.min(days, 30));\n\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"Cache-Control\": \"no-cache\",\n });\n res.end(JSON.stringify(stats, null, 2));\n } catch (err) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: `Failed to get stats: ${err instanceof Error ? err.message : String(err)}`,\n }),\n );\n }\n return;\n }\n\n // --- Handle /v1/models locally (no upstream call needed) ---\n if (req.url === \"/v1/models\" && req.method === \"GET\") {\n const models = BLOCKRUN_MODELS.filter((m) => m.id !== \"blockrun/auto\").map((m) => ({\n id: m.id,\n object: \"model\",\n created: Math.floor(Date.now() / 1000),\n owned_by: m.id.split(\"/\")[0] || \"unknown\",\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ object: \"list\", data: models }));\n return;\n }\n\n // Only proxy paths starting with /v1\n if (!req.url?.startsWith(\"/v1\")) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n try {\n await proxyRequest(\n req,\n res,\n apiBase,\n payFetch,\n options,\n routerOpts,\n deduplicator,\n balanceMonitor,\n sessionStore,\n );\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n options.onError?.(error);\n\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: { message: `Proxy error: ${error.message}`, type: \"proxy_error\" },\n }),\n );\n } else if (!res.writableEnded) {\n // Headers already sent (streaming) — send error as SSE event\n res.write(\n `data: ${JSON.stringify({ error: { message: error.message, type: \"proxy_error\" } })}\\n\\n`,\n );\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n }\n }\n });\n\n // Listen on configured port with retry logic for TIME_WAIT handling\n // When gateway restarts quickly, the port may still be in TIME_WAIT state.\n // We retry with delay instead of incorrectly assuming a proxy is running.\n const tryListen = (attempt: number): Promise<void> => {\n return new Promise<void>((resolveAttempt, rejectAttempt) => {\n const onError = async (err: NodeJS.ErrnoException) => {\n server.removeListener(\"error\", onError);\n\n if (err.code === \"EADDRINUSE\") {\n // Port is in use - check if a proxy is actually running\n const existingWallet = await checkExistingProxy(listenPort);\n if (existingWallet) {\n // Proxy is actually running - this is fine, reuse it\n console.log(`[ClawRouter] Existing proxy detected on port ${listenPort}, reusing`);\n rejectAttempt({ code: \"REUSE_EXISTING\", wallet: existingWallet });\n return;\n }\n\n // Port is in TIME_WAIT (no proxy responding) - retry after delay\n if (attempt < PORT_RETRY_ATTEMPTS) {\n console.log(\n `[ClawRouter] Port ${listenPort} in TIME_WAIT, retrying in ${PORT_RETRY_DELAY_MS}ms (attempt ${attempt}/${PORT_RETRY_ATTEMPTS})`,\n );\n rejectAttempt({ code: \"RETRY\", attempt });\n return;\n }\n\n // Max retries exceeded\n console.error(\n `[ClawRouter] Port ${listenPort} still in use after ${PORT_RETRY_ATTEMPTS} attempts`,\n );\n rejectAttempt(err);\n return;\n }\n\n rejectAttempt(err);\n };\n\n server.once(\"error\", onError);\n server.listen(listenPort, \"127.0.0.1\", () => {\n server.removeListener(\"error\", onError);\n resolveAttempt();\n });\n });\n };\n\n // Retry loop for port binding\n let lastError: Error | undefined;\n for (let attempt = 1; attempt <= PORT_RETRY_ATTEMPTS; attempt++) {\n try {\n await tryListen(attempt);\n break; // Success\n } catch (err: unknown) {\n const error = err as { code?: string; wallet?: string; attempt?: number };\n\n if (error.code === \"REUSE_EXISTING\" && error.wallet) {\n // Proxy is running, reuse it\n const baseUrl = `http://127.0.0.1:${listenPort}`;\n options.onReady?.(listenPort);\n return {\n port: listenPort,\n baseUrl,\n walletAddress: error.wallet,\n balanceMonitor,\n close: async () => {\n // No-op: we didn't start this proxy, so we shouldn't close it\n },\n };\n }\n\n if (error.code === \"RETRY\") {\n // Wait before retry\n await new Promise((r) => setTimeout(r, PORT_RETRY_DELAY_MS));\n continue;\n }\n\n // Other error - throw\n lastError = err as Error;\n break;\n }\n }\n\n if (lastError) {\n throw lastError;\n }\n\n // Server is now listening - set up remaining handlers\n const addr = server.address() as AddressInfo;\n const port = addr.port;\n const baseUrl = `http://127.0.0.1:${port}`;\n\n options.onReady?.(port);\n\n // Add runtime error handler AFTER successful listen\n // This handles errors that occur during server operation (not just startup)\n server.on(\"error\", (err) => {\n console.error(`[ClawRouter] Server runtime error: ${err.message}`);\n options.onError?.(err);\n // Don't crash - log and continue\n });\n\n // Handle client connection errors (bad requests, socket errors)\n server.on(\"clientError\", (err, socket) => {\n console.error(`[ClawRouter] Client error: ${err.message}`);\n // Send 400 Bad Request if socket is still writable\n if (socket.writable && !socket.destroyed) {\n socket.end(\"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n }\n });\n\n // Track connections for graceful cleanup\n server.on(\"connection\", (socket) => {\n connections.add(socket);\n\n // Set 5-minute timeout for streaming requests\n socket.setTimeout(300_000);\n\n socket.on(\"timeout\", () => {\n console.error(`[ClawRouter] Socket timeout, destroying connection`);\n socket.destroy();\n });\n\n socket.on(\"end\", () => {\n // Half-closed by client (FIN received)\n });\n\n socket.on(\"error\", (err) => {\n console.error(`[ClawRouter] Socket error: ${err.message}`);\n });\n\n socket.on(\"close\", () => {\n connections.delete(socket);\n });\n });\n\n return {\n port,\n baseUrl,\n walletAddress: account.address,\n balanceMonitor,\n close: () =>\n new Promise<void>((res, rej) => {\n const timeout = setTimeout(() => {\n rej(new Error(\"[ClawRouter] Close timeout after 4s\"));\n }, 4000);\n\n sessionStore.close();\n // Destroy all active connections before closing server\n for (const socket of connections) {\n socket.destroy();\n }\n connections.clear();\n server.close((err) => {\n clearTimeout(timeout);\n if (err) {\n rej(err);\n } else {\n res();\n }\n });\n }),\n };\n}\n\n/** Result of attempting a model request */\ntype ModelRequestResult = {\n success: boolean;\n response?: Response;\n errorBody?: string;\n errorStatus?: number;\n isProviderError?: boolean;\n};\n\n/**\n * Attempt a request with a specific model.\n * Returns the response or error details for fallback decision.\n */\nasync function tryModelRequest(\n upstreamUrl: string,\n method: string,\n headers: Record<string, string>,\n body: Buffer,\n modelId: string,\n maxTokens: number,\n payFetch: (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ) => Promise<Response>,\n balanceMonitor: BalanceMonitor,\n signal: AbortSignal,\n): Promise<ModelRequestResult> {\n // Update model in body and normalize messages\n let requestBody = body;\n try {\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n parsed.model = modelId;\n\n // Normalize message roles (e.g., \"developer\" -> \"system\")\n if (Array.isArray(parsed.messages)) {\n parsed.messages = normalizeMessageRoles(parsed.messages as ChatMessage[]);\n }\n\n // Sanitize tool IDs to match Anthropic's pattern (alphanumeric, underscore, hyphen only)\n if (Array.isArray(parsed.messages)) {\n parsed.messages = sanitizeToolIds(parsed.messages as ChatMessage[]);\n }\n\n // Normalize messages for Google models (first non-system message must be \"user\")\n if (isGoogleModel(modelId) && Array.isArray(parsed.messages)) {\n parsed.messages = normalizeMessagesForGoogle(parsed.messages as ChatMessage[]);\n }\n\n // Normalize messages for thinking-enabled requests (add reasoning_content to tool calls)\n // Check request flags AND target model - reasoning models have thinking enabled server-side\n const hasThinkingEnabled = !!(\n parsed.thinking ||\n parsed.extended_thinking ||\n isReasoningModel(modelId)\n );\n if (hasThinkingEnabled && Array.isArray(parsed.messages)) {\n parsed.messages = normalizeMessagesForThinking(parsed.messages as ExtendedChatMessage[]);\n }\n\n requestBody = Buffer.from(JSON.stringify(parsed));\n } catch {\n // If body isn't valid JSON, use as-is\n }\n\n // Estimate cost for pre-auth\n const estimated = estimateAmount(modelId, requestBody.length, maxTokens);\n const preAuth: PreAuthParams | undefined = estimated ? { estimatedAmount: estimated } : undefined;\n\n try {\n const response = await payFetch(\n upstreamUrl,\n {\n method,\n headers,\n body: requestBody.length > 0 ? new Uint8Array(requestBody) : undefined,\n signal,\n },\n preAuth,\n );\n\n // Check for provider errors\n if (response.status !== 200) {\n // Clone response to read body without consuming it\n const errorBody = await response.text();\n const isProviderErr = isProviderError(response.status, errorBody);\n\n return {\n success: false,\n errorBody,\n errorStatus: response.status,\n isProviderError: isProviderErr,\n };\n }\n\n return { success: true, response };\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n return {\n success: false,\n errorBody: errorMsg,\n errorStatus: 500,\n isProviderError: true, // Network errors are retryable\n };\n }\n}\n\n/**\n * Proxy a single request through x402 payment flow to BlockRun API.\n *\n * Optimizations applied in order:\n * 1. Dedup check — if same request body seen within 30s, replay cached response\n * 2. Streaming heartbeat — for stream:true, send 200 + heartbeats immediately\n * 3. Payment pre-auth — estimate USDC amount and pre-sign to skip 402 round trip\n * 4. Smart routing — when model is \"blockrun/auto\", pick cheapest capable model\n * 5. Fallback chain — on provider errors, try next model in tier's fallback list\n */\nasync function proxyRequest(\n req: IncomingMessage,\n res: ServerResponse,\n apiBase: string,\n payFetch: (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ) => Promise<Response>,\n options: ProxyOptions,\n routerOpts: RouterOptions,\n deduplicator: RequestDeduplicator,\n balanceMonitor: BalanceMonitor,\n sessionStore: SessionStore,\n): Promise<void> {\n const startTime = Date.now();\n\n // Build upstream URL: /v1/chat/completions → https://blockrun.ai/api/v1/chat/completions\n const upstreamUrl = `${apiBase}${req.url}`;\n\n // Collect request body\n const bodyChunks: Buffer[] = [];\n for await (const chunk of req) {\n bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n let body = Buffer.concat(bodyChunks);\n\n // --- Smart routing ---\n let routingDecision: RoutingDecision | undefined;\n let isStreaming = false;\n let modelId = \"\";\n let maxTokens = 4096;\n const isChatCompletion = req.url?.includes(\"/chat/completions\");\n\n if (isChatCompletion && body.length > 0) {\n try {\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n isStreaming = parsed.stream === true;\n modelId = (parsed.model as string) || \"\";\n maxTokens = (parsed.max_tokens as number) || 4096;\n\n // Force stream: false — BlockRun API doesn't support streaming yet\n // ClawRouter handles SSE heartbeat simulation for upstream compatibility\n let bodyModified = false;\n if (parsed.stream === true) {\n parsed.stream = false;\n bodyModified = true;\n }\n\n // Normalize model name for comparison (trim whitespace, lowercase)\n const normalizedModel =\n typeof parsed.model === \"string\" ? parsed.model.trim().toLowerCase() : \"\";\n\n // Resolve model aliases (e.g., \"claude\" -> \"anthropic/claude-sonnet-4\")\n const resolvedModel = resolveModelAlias(normalizedModel);\n const wasAlias = resolvedModel !== normalizedModel;\n\n const isAutoModel =\n normalizedModel === AUTO_MODEL.toLowerCase() ||\n normalizedModel === AUTO_MODEL_SHORT.toLowerCase();\n\n // Debug: log received model name\n console.log(\n `[ClawRouter] Received model: \"${parsed.model}\" -> normalized: \"${normalizedModel}\"${wasAlias ? ` -> alias: \"${resolvedModel}\"` : \"\"}, isAuto: ${isAutoModel}`,\n );\n\n // If alias was resolved, update the model in the request\n if (wasAlias && !isAutoModel) {\n parsed.model = resolvedModel;\n modelId = resolvedModel;\n bodyModified = true;\n }\n\n if (isAutoModel) {\n // Check for session persistence - use pinned model if available\n const sessionId = getSessionId(\n req.headers as Record<string, string | string[] | undefined>,\n );\n const existingSession = sessionId ? sessionStore.getSession(sessionId) : undefined;\n\n if (existingSession) {\n // Use the session's pinned model instead of re-routing\n console.log(\n `[ClawRouter] Session ${sessionId?.slice(0, 8)}... using pinned model: ${existingSession.model}`,\n );\n parsed.model = existingSession.model;\n modelId = existingSession.model;\n bodyModified = true;\n sessionStore.touchSession(sessionId!);\n } else {\n // No session or expired - route normally\n // Extract prompt from messages\n type ChatMessage = { role: string; content: string };\n const messages = parsed.messages as ChatMessage[] | undefined;\n let lastUserMsg: ChatMessage | undefined;\n if (messages) {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"user\") {\n lastUserMsg = messages[i];\n break;\n }\n }\n }\n const systemMsg = messages?.find((m: ChatMessage) => m.role === \"system\");\n const prompt = typeof lastUserMsg?.content === \"string\" ? lastUserMsg.content : \"\";\n const systemPrompt =\n typeof systemMsg?.content === \"string\" ? systemMsg.content : undefined;\n\n // Tool detection no longer forces agentic mode\n // Agentic mode is now triggered by keyword-based detection (agenticScore >= 0.6)\n // This allows simple queries with tools to use cheaper models\n const tools = parsed.tools as unknown[] | undefined;\n const hasTools = Array.isArray(tools) && tools.length > 0;\n\n if (hasTools) {\n console.log(`[ClawRouter] Tools detected (${tools.length}), agentic mode via keywords`);\n }\n\n routingDecision = route(prompt, systemPrompt, maxTokens, routerOpts);\n\n // Replace model in body\n parsed.model = routingDecision.model;\n modelId = routingDecision.model;\n bodyModified = true;\n\n // Pin this model to the session for future requests\n if (sessionId) {\n sessionStore.setSession(sessionId, routingDecision.model, routingDecision.tier);\n console.log(\n `[ClawRouter] Session ${sessionId.slice(0, 8)}... pinned to model: ${routingDecision.model}`,\n );\n }\n\n options.onRouted?.(routingDecision);\n }\n }\n\n // Rebuild body if modified\n if (bodyModified) {\n body = Buffer.from(JSON.stringify(parsed));\n }\n } catch (err) {\n // Log routing errors so they're not silently swallowed\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(`[ClawRouter] Routing error: ${errorMsg}`);\n options.onError?.(new Error(`Routing failed: ${errorMsg}`));\n }\n }\n\n // --- Dedup check ---\n const dedupKey = RequestDeduplicator.hash(body);\n\n // Check completed cache first\n const cached = deduplicator.getCached(dedupKey);\n if (cached) {\n res.writeHead(cached.status, cached.headers);\n res.end(cached.body);\n return;\n }\n\n // Check in-flight — wait for the original request to complete\n const inflight = deduplicator.getInflight(dedupKey);\n if (inflight) {\n const result = await inflight;\n res.writeHead(result.status, result.headers);\n res.end(result.body);\n return;\n }\n\n // Register this request as in-flight\n deduplicator.markInflight(dedupKey);\n\n // --- Pre-request balance check ---\n // Estimate cost and check if wallet has sufficient balance\n // Skip if skipBalanceCheck is set (for testing) or if using free model\n let estimatedCostMicros: bigint | undefined;\n const isFreeModel = modelId === FREE_MODEL;\n\n if (modelId && !options.skipBalanceCheck && !isFreeModel) {\n const estimated = estimateAmount(modelId, body.length, maxTokens);\n if (estimated) {\n estimatedCostMicros = BigInt(estimated);\n\n // Apply extra buffer for balance check to prevent x402 failures after streaming starts.\n // This is aggressive to avoid triggering OpenClaw's 5-24 hour billing cooldown.\n const bufferedCostMicros =\n (estimatedCostMicros * BigInt(Math.ceil(BALANCE_CHECK_BUFFER * 100))) / 100n;\n\n // Check balance before proceeding (using buffered amount)\n const sufficiency = await balanceMonitor.checkSufficient(bufferedCostMicros);\n\n if (sufficiency.info.isEmpty || !sufficiency.sufficient) {\n // Wallet is empty or insufficient — ALWAYS fallback to free model\n // This ensures new users with empty wallets can still use ClawRouter\n const originalModel = modelId;\n console.log(\n `[ClawRouter] Wallet ${sufficiency.info.isEmpty ? \"empty\" : \"insufficient\"} ($${sufficiency.info.balanceUSD}), falling back to free model: ${FREE_MODEL} (requested: ${originalModel})`,\n );\n modelId = FREE_MODEL;\n // Update the body with new model\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n parsed.model = FREE_MODEL;\n body = Buffer.from(JSON.stringify(parsed));\n\n // Notify about the fallback\n options.onLowBalance?.({\n balanceUSD: sufficiency.info.balanceUSD,\n walletAddress: sufficiency.info.walletAddress,\n });\n } else if (sufficiency.info.isLow) {\n // Balance is low but sufficient — warn and proceed\n options.onLowBalance?.({\n balanceUSD: sufficiency.info.balanceUSD,\n walletAddress: sufficiency.info.walletAddress,\n });\n }\n }\n }\n\n // --- Streaming: early header flush + heartbeat ---\n let heartbeatInterval: ReturnType<typeof setInterval> | undefined;\n let headersSentEarly = false;\n\n if (isStreaming) {\n // Send 200 + SSE headers immediately, before x402 flow\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n });\n headersSentEarly = true;\n\n // First heartbeat immediately\n safeWrite(res, \": heartbeat\\n\\n\");\n\n // Continue heartbeats every 2s while waiting for upstream\n heartbeatInterval = setInterval(() => {\n if (canWrite(res)) {\n safeWrite(res, \": heartbeat\\n\\n\");\n } else {\n // Socket closed, stop heartbeat\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n\n // Forward headers, stripping host, connection, and content-length\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (\n key === \"host\" ||\n key === \"connection\" ||\n key === \"transfer-encoding\" ||\n key === \"content-length\"\n )\n continue;\n if (typeof value === \"string\") {\n headers[key] = value;\n }\n }\n if (!headers[\"content-type\"]) {\n headers[\"content-type\"] = \"application/json\";\n }\n headers[\"user-agent\"] = USER_AGENT;\n\n // --- Client disconnect cleanup ---\n let completed = false;\n res.on(\"close\", () => {\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n // Remove from in-flight if client disconnected before completion\n if (!completed) {\n deduplicator.removeInflight(dedupKey);\n }\n });\n\n // --- Request timeout ---\n const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n // --- Build fallback chain ---\n // If we have a routing decision, get the full fallback chain for the tier\n // Otherwise, just use the current model (no fallback for explicit model requests)\n let modelsToTry: string[];\n if (routingDecision) {\n // Estimate total context: input tokens (~4 chars per token) + max output tokens\n const estimatedInputTokens = Math.ceil(body.length / 4);\n const estimatedTotalTokens = estimatedInputTokens + maxTokens;\n\n // Get tier configs (use agentic tiers if routing decided to use them)\n const useAgenticTiers =\n routingDecision.reasoning?.includes(\"agentic\") && routerOpts.config.agenticTiers;\n const tierConfigs = useAgenticTiers\n ? routerOpts.config.agenticTiers!\n : routerOpts.config.tiers;\n\n // Get full chain first, then filter by context\n const fullChain = getFallbackChain(routingDecision.tier, tierConfigs);\n const contextFiltered = getFallbackChainFiltered(\n routingDecision.tier,\n tierConfigs,\n estimatedTotalTokens,\n getModelContextWindow,\n );\n\n // Log if models were filtered out due to context limits\n const contextExcluded = fullChain.filter((m) => !contextFiltered.includes(m));\n if (contextExcluded.length > 0) {\n console.log(\n `[ClawRouter] Context filter (~${estimatedTotalTokens} tokens): excluded ${contextExcluded.join(\", \")}`,\n );\n }\n\n // Limit to MAX_FALLBACK_ATTEMPTS to prevent infinite loops\n modelsToTry = contextFiltered.slice(0, MAX_FALLBACK_ATTEMPTS);\n\n // Deprioritize rate-limited models (put them at the end)\n modelsToTry = prioritizeNonRateLimited(modelsToTry);\n } else {\n // For explicit model requests, add free model as emergency fallback\n // in case the primary model fails due to insufficient funds mid-request\n if (modelId && modelId !== FREE_MODEL) {\n modelsToTry = [modelId, FREE_MODEL];\n } else {\n modelsToTry = modelId ? [modelId] : [];\n }\n }\n\n // --- Fallback loop: try each model until success ---\n let upstream: Response | undefined;\n let lastError: { body: string; status: number } | undefined;\n let actualModelUsed = modelId;\n\n for (let i = 0; i < modelsToTry.length; i++) {\n const tryModel = modelsToTry[i];\n const isLastAttempt = i === modelsToTry.length - 1;\n\n console.log(`[ClawRouter] Trying model ${i + 1}/${modelsToTry.length}: ${tryModel}`);\n\n const result = await tryModelRequest(\n upstreamUrl,\n req.method ?? \"POST\",\n headers,\n body,\n tryModel,\n maxTokens,\n payFetch,\n balanceMonitor,\n controller.signal,\n );\n\n if (result.success && result.response) {\n upstream = result.response;\n actualModelUsed = tryModel;\n console.log(`[ClawRouter] Success with model: ${tryModel}`);\n break;\n }\n\n // Request failed\n lastError = {\n body: result.errorBody || \"Unknown error\",\n status: result.errorStatus || 500,\n };\n\n // If it's a provider error and not the last attempt, try next model\n if (result.isProviderError && !isLastAttempt) {\n // Track 429 rate limits to deprioritize this model for future requests\n if (result.errorStatus === 429) {\n markRateLimited(tryModel);\n }\n console.log(\n `[ClawRouter] Provider error from ${tryModel}, trying fallback: ${result.errorBody?.slice(0, 100)}`,\n );\n continue;\n }\n\n // Not a provider error or last attempt — stop trying\n if (!result.isProviderError) {\n console.log(\n `[ClawRouter] Non-provider error from ${tryModel}, not retrying: ${result.errorBody?.slice(0, 100)}`,\n );\n }\n break;\n }\n\n // Clear timeout — request attempts completed\n clearTimeout(timeoutId);\n\n // Clear heartbeat — real data is about to flow\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n\n // Update routing decision with actual model used (for logging)\n // IMPORTANT: Recalculate cost for the actual model, not the original primary\n if (routingDecision && actualModelUsed !== routingDecision.model) {\n const estimatedInputTokens = Math.ceil(body.length / 4);\n const newCosts = calculateModelCost(\n actualModelUsed,\n routerOpts.modelPricing,\n estimatedInputTokens,\n maxTokens,\n );\n routingDecision = {\n ...routingDecision,\n model: actualModelUsed,\n reasoning: `${routingDecision.reasoning} | fallback to ${actualModelUsed}`,\n costEstimate: newCosts.costEstimate,\n baselineCost: newCosts.baselineCost,\n savings: newCosts.savings,\n };\n options.onRouted?.(routingDecision);\n }\n\n // --- Handle case where all models failed ---\n if (!upstream) {\n const rawErrBody = lastError?.body || \"All models in fallback chain failed\";\n const errStatus = lastError?.status || 502;\n\n // Transform payment errors into user-friendly messages\n const transformedErr = transformPaymentError(rawErrBody);\n\n if (headersSentEarly) {\n // Streaming: send error as SSE event\n // If transformed error is already JSON, parse and use it; otherwise wrap in standard format\n let errPayload: string;\n try {\n const parsed = JSON.parse(transformedErr);\n errPayload = JSON.stringify(parsed);\n } catch {\n errPayload = JSON.stringify({\n error: { message: rawErrBody, type: \"provider_error\", status: errStatus },\n });\n }\n const errEvent = `data: ${errPayload}\\n\\n`;\n safeWrite(res, errEvent);\n safeWrite(res, \"data: [DONE]\\n\\n\");\n res.end();\n\n const errBuf = Buffer.from(errEvent + \"data: [DONE]\\n\\n\");\n deduplicator.complete(dedupKey, {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n body: errBuf,\n completedAt: Date.now(),\n });\n } else {\n // Non-streaming: send transformed error response\n res.writeHead(errStatus, { \"Content-Type\": \"application/json\" });\n res.end(transformedErr);\n\n deduplicator.complete(dedupKey, {\n status: errStatus,\n headers: { \"content-type\": \"application/json\" },\n body: Buffer.from(transformedErr),\n completedAt: Date.now(),\n });\n }\n return;\n }\n\n // --- Stream response and collect for dedup cache ---\n const responseChunks: Buffer[] = [];\n\n if (headersSentEarly) {\n // Streaming: headers already sent. Response should be 200 at this point\n // (non-200 responses are handled in the fallback loop above)\n\n // Convert non-streaming JSON response to SSE streaming format for client\n // (BlockRun API returns JSON since we forced stream:false)\n // OpenClaw expects: object=\"chat.completion.chunk\" with choices[].delta (not message)\n // We emit proper incremental deltas to match OpenAI's streaming format exactly\n if (upstream.body) {\n const reader = upstream.body.getReader();\n const chunks: Uint8Array[] = [];\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n // Combine chunks and transform to streaming format\n const jsonBody = Buffer.concat(chunks);\n const jsonStr = jsonBody.toString();\n try {\n const rsp = JSON.parse(jsonStr) as {\n id?: string;\n object?: string;\n created?: number;\n model?: string;\n choices?: Array<{\n index?: number;\n message?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n id: string;\n type: string;\n function: { name: string; arguments: string };\n }>;\n };\n delta?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n id: string;\n type: string;\n function: { name: string; arguments: string };\n }>;\n };\n finish_reason?: string | null;\n }>;\n usage?: unknown;\n };\n\n // Build base chunk structure (reused for all chunks)\n // Match OpenAI's exact format including system_fingerprint\n const baseChunk = {\n id: rsp.id ?? `chatcmpl-${Date.now()}`,\n object: \"chat.completion.chunk\",\n created: rsp.created ?? Math.floor(Date.now() / 1000),\n model: rsp.model ?? \"unknown\",\n system_fingerprint: null,\n };\n\n // Process each choice (usually just one)\n if (rsp.choices && Array.isArray(rsp.choices)) {\n for (const choice of rsp.choices) {\n // Strip thinking tokens (Kimi <|...|> and standard <think> tags)\n const rawContent = choice.message?.content ?? choice.delta?.content ?? \"\";\n const content = stripThinkingTokens(rawContent);\n const role = choice.message?.role ?? choice.delta?.role ?? \"assistant\";\n const index = choice.index ?? 0;\n\n // Chunk 1: role only (mimics OpenAI's first chunk)\n const roleChunk = {\n ...baseChunk,\n choices: [{ index, delta: { role }, logprobs: null, finish_reason: null }],\n };\n const roleData = `data: ${JSON.stringify(roleChunk)}\\n\\n`;\n safeWrite(res, roleData);\n responseChunks.push(Buffer.from(roleData));\n\n // Chunk 2: content (single chunk with full content)\n if (content) {\n const contentChunk = {\n ...baseChunk,\n choices: [{ index, delta: { content }, logprobs: null, finish_reason: null }],\n };\n const contentData = `data: ${JSON.stringify(contentChunk)}\\n\\n`;\n safeWrite(res, contentData);\n responseChunks.push(Buffer.from(contentData));\n }\n\n // Chunk 2b: tool_calls (forward tool calls from upstream)\n const toolCalls = choice.message?.tool_calls ?? choice.delta?.tool_calls;\n if (toolCalls && toolCalls.length > 0) {\n const toolCallChunk = {\n ...baseChunk,\n choices: [\n {\n index,\n delta: { tool_calls: toolCalls },\n logprobs: null,\n finish_reason: null,\n },\n ],\n };\n const toolCallData = `data: ${JSON.stringify(toolCallChunk)}\\n\\n`;\n safeWrite(res, toolCallData);\n responseChunks.push(Buffer.from(toolCallData));\n }\n\n // Chunk 3: finish_reason (signals completion)\n const finishChunk = {\n ...baseChunk,\n choices: [\n {\n index,\n delta: {},\n logprobs: null,\n finish_reason:\n toolCalls && toolCalls.length > 0\n ? \"tool_calls\"\n : (choice.finish_reason ?? \"stop\"),\n },\n ],\n };\n const finishData = `data: ${JSON.stringify(finishChunk)}\\n\\n`;\n safeWrite(res, finishData);\n responseChunks.push(Buffer.from(finishData));\n }\n }\n } catch {\n // If parsing fails, send raw response as single chunk\n const sseData = `data: ${jsonStr}\\n\\n`;\n safeWrite(res, sseData);\n responseChunks.push(Buffer.from(sseData));\n }\n }\n\n // Send SSE terminator\n safeWrite(res, \"data: [DONE]\\n\\n\");\n responseChunks.push(Buffer.from(\"data: [DONE]\\n\\n\"));\n res.end();\n\n // Cache for dedup\n deduplicator.complete(dedupKey, {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n body: Buffer.concat(responseChunks),\n completedAt: Date.now(),\n });\n } else {\n // Non-streaming: forward status and headers from upstream\n const responseHeaders: Record<string, string> = {};\n upstream.headers.forEach((value, key) => {\n // Skip hop-by-hop headers and content-encoding (fetch already decompresses)\n if (key === \"transfer-encoding\" || key === \"connection\" || key === \"content-encoding\")\n return;\n responseHeaders[key] = value;\n });\n\n res.writeHead(upstream.status, responseHeaders);\n\n if (upstream.body) {\n const reader = upstream.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunk = Buffer.from(value);\n safeWrite(res, chunk);\n responseChunks.push(chunk);\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n res.end();\n\n // Cache for dedup\n deduplicator.complete(dedupKey, {\n status: upstream.status,\n headers: responseHeaders,\n body: Buffer.concat(responseChunks),\n completedAt: Date.now(),\n });\n }\n\n // --- Optimistic balance deduction after successful response ---\n if (estimatedCostMicros !== undefined) {\n balanceMonitor.deductEstimated(estimatedCostMicros);\n }\n\n // Mark request as completed (for client disconnect cleanup)\n completed = true;\n } catch (err) {\n // Clear timeout on error\n clearTimeout(timeoutId);\n\n // Clear heartbeat on error\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n\n // Remove in-flight entry so retries aren't blocked\n deduplicator.removeInflight(dedupKey);\n\n // Invalidate balance cache on payment failure (might be out of date)\n balanceMonitor.invalidate();\n\n // Convert abort error to more descriptive timeout error\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new Error(`Request timed out after ${timeoutMs}ms`);\n }\n\n throw err;\n }\n\n // --- Usage logging (fire-and-forget) ---\n // Note: Recalculate cost using full body length (not just system+user message)\n // and apply 20% buffer to match actual x402 payment (see estimateAmount())\n if (routingDecision) {\n // Use full body length for accurate cost (matches x402 payment estimation)\n const estimatedInputTokens = Math.ceil(body.length / 4);\n const accurateCosts = calculateModelCost(\n routingDecision.model,\n routerOpts.modelPricing,\n estimatedInputTokens,\n maxTokens,\n );\n // Apply 20% buffer to match x402 pre-auth\n const costWithBuffer = accurateCosts.costEstimate * 1.2;\n const baselineWithBuffer = accurateCosts.baselineCost * 1.2;\n const entry: UsageEntry = {\n timestamp: new Date().toISOString(),\n model: routingDecision.model,\n tier: routingDecision.tier,\n cost: costWithBuffer,\n baselineCost: baselineWithBuffer,\n savings: accurateCosts.savings,\n latencyMs: Date.now() - startTime,\n };\n logUsage(entry).catch(() => {});\n }\n}\n","/**\n * x402 Payment Implementation\n *\n * Based on BlockRun's proven implementation.\n * Handles 402 Payment Required responses with EIP-712 signed USDC transfers.\n *\n * Optimizations (v0.3.0):\n * - Payment cache: after first 402, caches {payTo, asset, network} per endpoint.\n * On subsequent requests, pre-signs payment and sends with first request,\n * skipping the 402 round trip (~200ms savings).\n * - Falls back to normal 402 flow if pre-signed payment is rejected.\n */\n\nimport { signTypedData, privateKeyToAccount } from \"viem/accounts\";\nimport { PaymentCache } from \"./payment-cache.js\";\n\nconst BASE_CHAIN_ID = 8453;\nconst USDC_BASE = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\" as const;\n\nconst USDC_DOMAIN = {\n name: \"USD Coin\",\n version: \"2\",\n chainId: BASE_CHAIN_ID,\n verifyingContract: USDC_BASE,\n} as const;\n\nconst TRANSFER_TYPES = {\n TransferWithAuthorization: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n ],\n} as const;\n\nfunction createNonce(): `0x${string}` {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}` as `0x${string}`;\n}\n\ninterface PaymentOption {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: { name?: string; version?: string };\n}\n\ninterface PaymentRequired {\n accepts: PaymentOption[];\n resource?: { url?: string; description?: string };\n}\n\nfunction parsePaymentRequired(headerValue: string): PaymentRequired {\n const decoded = atob(headerValue);\n return JSON.parse(decoded) as PaymentRequired;\n}\n\nasync function createPaymentPayload(\n privateKey: `0x${string}`,\n fromAddress: string,\n recipient: string,\n amount: string,\n resourceUrl: string,\n): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const validAfter = now - 600;\n const validBefore = now + 300;\n const nonce = createNonce();\n\n const signature = await signTypedData({\n privateKey,\n domain: USDC_DOMAIN,\n types: TRANSFER_TYPES,\n primaryType: \"TransferWithAuthorization\",\n message: {\n from: fromAddress as `0x${string}`,\n to: recipient as `0x${string}`,\n value: BigInt(amount),\n validAfter: BigInt(validAfter),\n validBefore: BigInt(validBefore),\n nonce,\n },\n });\n\n const paymentData = {\n x402Version: 2,\n resource: {\n url: resourceUrl,\n description: \"BlockRun AI API call\",\n mimeType: \"application/json\",\n },\n accepted: {\n scheme: \"exact\",\n network: \"eip155:8453\",\n amount,\n asset: USDC_BASE,\n payTo: recipient,\n maxTimeoutSeconds: 300,\n extra: { name: \"USD Coin\", version: \"2\" },\n },\n payload: {\n signature,\n authorization: {\n from: fromAddress,\n to: recipient,\n value: amount,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n },\n },\n extensions: {},\n };\n\n return btoa(JSON.stringify(paymentData));\n}\n\n/** Pre-auth parameters for skipping the 402 round trip. */\nexport type PreAuthParams = {\n estimatedAmount: string; // USDC amount in smallest unit (6 decimals)\n};\n\n/** Result from createPaymentFetch — includes the fetch wrapper and payment cache. */\nexport type PaymentFetchResult = {\n fetch: (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ) => Promise<Response>;\n cache: PaymentCache;\n};\n\n/**\n * Create a fetch wrapper that handles x402 payment automatically.\n *\n * Supports pre-auth: if cached payment params + estimated amount are available,\n * pre-signs and attaches payment to the first request, skipping the 402 round trip.\n * Falls back to normal 402 flow if pre-signed payment is rejected.\n */\nexport function createPaymentFetch(privateKey: `0x${string}`): PaymentFetchResult {\n const account = privateKeyToAccount(privateKey);\n const walletAddress = account.address;\n const paymentCache = new PaymentCache();\n\n const payFetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const endpointPath = new URL(url).pathname;\n\n // --- Pre-auth path: skip 402 round trip ---\n const cached = paymentCache.get(endpointPath);\n if (cached && preAuth?.estimatedAmount) {\n const paymentPayload = await createPaymentPayload(\n privateKey,\n walletAddress,\n cached.payTo,\n preAuth.estimatedAmount,\n url,\n );\n\n const preAuthHeaders = new Headers(init?.headers);\n preAuthHeaders.set(\"payment-signature\", paymentPayload);\n\n const response = await fetch(input, { ...init, headers: preAuthHeaders });\n\n // Pre-auth accepted — skip 402 entirely\n if (response.status !== 402) {\n return response;\n }\n\n // Pre-auth rejected (wrong amount, payTo changed, etc.)\n // Try to use this 402's payment header for a proper retry\n const paymentHeader = response.headers.get(\"x-payment-required\");\n if (paymentHeader) {\n return handle402(input, init, url, endpointPath, paymentHeader);\n }\n\n // No payment header — invalidate cache and retry clean (no payment header)\n // to get a proper 402 with payment requirements\n paymentCache.invalidate(endpointPath);\n const cleanResponse = await fetch(input, init);\n if (cleanResponse.status !== 402) {\n return cleanResponse;\n }\n const cleanHeader = cleanResponse.headers.get(\"x-payment-required\");\n if (!cleanHeader) {\n throw new Error(\"402 response missing x-payment-required header\");\n }\n return handle402(input, init, url, endpointPath, cleanHeader);\n }\n\n // --- Normal path: first request may get 402 ---\n const response = await fetch(input, init);\n\n if (response.status !== 402) {\n return response;\n }\n\n const paymentHeader = response.headers.get(\"x-payment-required\");\n if (!paymentHeader) {\n throw new Error(\"402 response missing x-payment-required header\");\n }\n\n return handle402(input, init, url, endpointPath, paymentHeader);\n };\n\n /** Handle a 402 response: parse, cache params, sign, retry. */\n async function handle402(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n url: string,\n endpointPath: string,\n paymentHeader: string,\n ): Promise<Response> {\n const paymentRequired = parsePaymentRequired(paymentHeader);\n const option = paymentRequired.accepts?.[0];\n if (!option) {\n throw new Error(\"No payment options in 402 response\");\n }\n\n const amount = option.amount || option.maxAmountRequired;\n if (!amount) {\n throw new Error(\"No amount in payment requirements\");\n }\n\n // Cache payment params for future pre-auth\n paymentCache.set(endpointPath, {\n payTo: option.payTo,\n asset: option.asset,\n scheme: option.scheme,\n network: option.network,\n extra: option.extra,\n maxTimeoutSeconds: option.maxTimeoutSeconds,\n });\n\n // Create signed payment\n const paymentPayload = await createPaymentPayload(\n privateKey,\n walletAddress,\n option.payTo,\n amount,\n url,\n );\n\n // Retry with payment\n const retryHeaders = new Headers(init?.headers);\n retryHeaders.set(\"payment-signature\", paymentPayload);\n\n return fetch(input, {\n ...init,\n headers: retryHeaders,\n });\n }\n\n return { fetch: payFetch, cache: paymentCache };\n}\n","/**\n * Payment Parameter Cache\n *\n * Caches the 402 payment parameters (payTo, asset, network, etc.) after the first\n * request to each endpoint. On subsequent requests, pre-signs the payment and\n * attaches it to the first request, skipping the 402 round trip (~200ms savings).\n */\n\nexport type CachedPaymentParams = {\n payTo: string;\n asset: string;\n scheme: string;\n network: string;\n extra?: { name?: string; version?: string };\n maxTimeoutSeconds?: number;\n cachedAt: number;\n};\n\nconst DEFAULT_TTL_MS = 3_600_000; // 1 hour\n\nexport class PaymentCache {\n private cache = new Map<string, CachedPaymentParams>();\n private ttlMs: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS) {\n this.ttlMs = ttlMs;\n }\n\n /** Get cached payment params for an endpoint path. */\n get(endpointPath: string): CachedPaymentParams | undefined {\n const entry = this.cache.get(endpointPath);\n if (!entry) return undefined;\n if (Date.now() - entry.cachedAt > this.ttlMs) {\n this.cache.delete(endpointPath);\n return undefined;\n }\n return entry;\n }\n\n /** Cache payment params from a 402 response. */\n set(endpointPath: string, params: Omit<CachedPaymentParams, \"cachedAt\">): void {\n this.cache.set(endpointPath, { ...params, cachedAt: Date.now() });\n }\n\n /** Invalidate cache for an endpoint (e.g., if payTo changed). */\n invalidate(endpointPath: string): void {\n this.cache.delete(endpointPath);\n }\n}\n","/**\n * Rule-Based Classifier (v2 — Weighted Scoring)\n *\n * Scores a request across 14 weighted dimensions and maps the aggregate\n * score to a tier using configurable boundaries. Confidence is calibrated\n * via sigmoid — low confidence triggers the fallback classifier.\n *\n * Handles 70-80% of requests in < 1ms with zero cost.\n */\n\nimport type { Tier, ScoringResult, ScoringConfig } from \"./types.js\";\n\ntype DimensionScore = { name: string; score: number; signal: string | null };\n\n// ─── Dimension Scorers ───\n// Each returns a score in [-1, 1] and an optional signal string.\n\nfunction scoreTokenCount(\n estimatedTokens: number,\n thresholds: { simple: number; complex: number },\n): DimensionScore {\n if (estimatedTokens < thresholds.simple) {\n return { name: \"tokenCount\", score: -1.0, signal: `short (${estimatedTokens} tokens)` };\n }\n if (estimatedTokens > thresholds.complex) {\n return { name: \"tokenCount\", score: 1.0, signal: `long (${estimatedTokens} tokens)` };\n }\n return { name: \"tokenCount\", score: 0, signal: null };\n}\n\nfunction scoreKeywordMatch(\n text: string,\n keywords: string[],\n name: string,\n signalLabel: string,\n thresholds: { low: number; high: number },\n scores: { none: number; low: number; high: number },\n): DimensionScore {\n const matches = keywords.filter((kw) => text.includes(kw.toLowerCase()));\n if (matches.length >= thresholds.high) {\n return {\n name,\n score: scores.high,\n signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})`,\n };\n }\n if (matches.length >= thresholds.low) {\n return {\n name,\n score: scores.low,\n signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})`,\n };\n }\n return { name, score: scores.none, signal: null };\n}\n\nfunction scoreMultiStep(text: string): DimensionScore {\n const patterns = [/first.*then/i, /step \\d/i, /\\d\\.\\s/];\n const hits = patterns.filter((p) => p.test(text));\n if (hits.length > 0) {\n return { name: \"multiStepPatterns\", score: 0.5, signal: \"multi-step\" };\n }\n return { name: \"multiStepPatterns\", score: 0, signal: null };\n}\n\nfunction scoreQuestionComplexity(prompt: string): DimensionScore {\n const count = (prompt.match(/\\?/g) || []).length;\n if (count > 3) {\n return { name: \"questionComplexity\", score: 0.5, signal: `${count} questions` };\n }\n return { name: \"questionComplexity\", score: 0, signal: null };\n}\n\n/**\n * Score agentic task indicators.\n * Returns agenticScore (0-1) based on keyword matches:\n * - 4+ matches = 1.0 (high agentic)\n * - 3 matches = 0.6 (moderate agentic, triggers auto-agentic mode)\n * - 1-2 matches = 0.2 (low agentic)\n *\n * Thresholds raised because common keywords were pruned from the list.\n */\nfunction scoreAgenticTask(\n text: string,\n keywords: string[],\n): { dimensionScore: DimensionScore; agenticScore: number } {\n let matchCount = 0;\n const signals: string[] = [];\n\n for (const keyword of keywords) {\n if (text.includes(keyword.toLowerCase())) {\n matchCount++;\n if (signals.length < 3) {\n signals.push(keyword);\n }\n }\n }\n\n // Threshold-based scoring (raised thresholds after keyword pruning)\n if (matchCount >= 4) {\n return {\n dimensionScore: {\n name: \"agenticTask\",\n score: 1.0,\n signal: `agentic (${signals.join(\", \")})`,\n },\n agenticScore: 1.0,\n };\n } else if (matchCount >= 3) {\n return {\n dimensionScore: {\n name: \"agenticTask\",\n score: 0.6,\n signal: `agentic (${signals.join(\", \")})`,\n },\n agenticScore: 0.6,\n };\n } else if (matchCount >= 1) {\n return {\n dimensionScore: {\n name: \"agenticTask\",\n score: 0.2,\n signal: `agentic-light (${signals.join(\", \")})`,\n },\n agenticScore: 0.2,\n };\n }\n\n return {\n dimensionScore: { name: \"agenticTask\", score: 0, signal: null },\n agenticScore: 0,\n };\n}\n\n// ─── Main Classifier ───\n\nexport function classifyByRules(\n prompt: string,\n systemPrompt: string | undefined,\n estimatedTokens: number,\n config: ScoringConfig,\n): ScoringResult {\n const text = `${systemPrompt ?? \"\"} ${prompt}`.toLowerCase();\n // User prompt only — used for reasoning markers (system prompt shouldn't influence complexity)\n const userText = prompt.toLowerCase();\n\n // Score all 14 dimensions\n const dimensions: DimensionScore[] = [\n // Original 8 dimensions\n scoreTokenCount(estimatedTokens, config.tokenCountThresholds),\n scoreKeywordMatch(\n text,\n config.codeKeywords,\n \"codePresence\",\n \"code\",\n { low: 1, high: 2 },\n { none: 0, low: 0.5, high: 1.0 },\n ),\n // Reasoning markers use USER prompt only — system prompt \"step by step\" shouldn't trigger reasoning\n scoreKeywordMatch(\n userText,\n config.reasoningKeywords,\n \"reasoningMarkers\",\n \"reasoning\",\n { low: 1, high: 2 },\n { none: 0, low: 0.7, high: 1.0 },\n ),\n scoreKeywordMatch(\n text,\n config.technicalKeywords,\n \"technicalTerms\",\n \"technical\",\n { low: 2, high: 4 },\n { none: 0, low: 0.5, high: 1.0 },\n ),\n scoreKeywordMatch(\n text,\n config.creativeKeywords,\n \"creativeMarkers\",\n \"creative\",\n { low: 1, high: 2 },\n { none: 0, low: 0.5, high: 0.7 },\n ),\n scoreKeywordMatch(\n text,\n config.simpleKeywords,\n \"simpleIndicators\",\n \"simple\",\n { low: 1, high: 2 },\n { none: 0, low: -1.0, high: -1.0 },\n ),\n scoreMultiStep(text),\n scoreQuestionComplexity(prompt),\n\n // 6 new dimensions\n scoreKeywordMatch(\n text,\n config.imperativeVerbs,\n \"imperativeVerbs\",\n \"imperative\",\n { low: 1, high: 2 },\n { none: 0, low: 0.3, high: 0.5 },\n ),\n scoreKeywordMatch(\n text,\n config.constraintIndicators,\n \"constraintCount\",\n \"constraints\",\n { low: 1, high: 3 },\n { none: 0, low: 0.3, high: 0.7 },\n ),\n scoreKeywordMatch(\n text,\n config.outputFormatKeywords,\n \"outputFormat\",\n \"format\",\n { low: 1, high: 2 },\n { none: 0, low: 0.4, high: 0.7 },\n ),\n scoreKeywordMatch(\n text,\n config.referenceKeywords,\n \"referenceComplexity\",\n \"references\",\n { low: 1, high: 2 },\n { none: 0, low: 0.3, high: 0.5 },\n ),\n scoreKeywordMatch(\n text,\n config.negationKeywords,\n \"negationComplexity\",\n \"negation\",\n { low: 2, high: 3 },\n { none: 0, low: 0.3, high: 0.5 },\n ),\n scoreKeywordMatch(\n text,\n config.domainSpecificKeywords,\n \"domainSpecificity\",\n \"domain-specific\",\n { low: 1, high: 2 },\n { none: 0, low: 0.5, high: 0.8 },\n ),\n ];\n\n // Score agentic task indicators\n const agenticResult = scoreAgenticTask(text, config.agenticTaskKeywords);\n dimensions.push(agenticResult.dimensionScore);\n const agenticScore = agenticResult.agenticScore;\n\n // Collect signals\n const signals = dimensions.filter((d) => d.signal !== null).map((d) => d.signal!);\n\n // Compute weighted score\n const weights = config.dimensionWeights;\n let weightedScore = 0;\n for (const d of dimensions) {\n const w = weights[d.name] ?? 0;\n weightedScore += d.score * w;\n }\n\n // Count reasoning markers for override — only check USER prompt, not system prompt\n // This prevents system prompts with \"step by step\" from triggering REASONING for simple queries\n const reasoningMatches = config.reasoningKeywords.filter((kw) =>\n userText.includes(kw.toLowerCase()),\n );\n\n // Direct reasoning override: 2+ reasoning markers = high confidence REASONING\n if (reasoningMatches.length >= 2) {\n const confidence = calibrateConfidence(\n Math.max(weightedScore, 0.3), // ensure positive for confidence calc\n config.confidenceSteepness,\n );\n return {\n score: weightedScore,\n tier: \"REASONING\",\n confidence: Math.max(confidence, 0.85),\n signals,\n agenticScore,\n };\n }\n\n // Map weighted score to tier using boundaries\n const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;\n let tier: Tier;\n let distanceFromBoundary: number;\n\n if (weightedScore < simpleMedium) {\n tier = \"SIMPLE\";\n distanceFromBoundary = simpleMedium - weightedScore;\n } else if (weightedScore < mediumComplex) {\n tier = \"MEDIUM\";\n distanceFromBoundary = Math.min(weightedScore - simpleMedium, mediumComplex - weightedScore);\n } else if (weightedScore < complexReasoning) {\n tier = \"COMPLEX\";\n distanceFromBoundary = Math.min(\n weightedScore - mediumComplex,\n complexReasoning - weightedScore,\n );\n } else {\n tier = \"REASONING\";\n distanceFromBoundary = weightedScore - complexReasoning;\n }\n\n // Calibrate confidence via sigmoid of distance from nearest boundary\n const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);\n\n // If confidence is below threshold → ambiguous\n if (confidence < config.confidenceThreshold) {\n return { score: weightedScore, tier: null, confidence, signals, agenticScore };\n }\n\n return { score: weightedScore, tier, confidence, signals, agenticScore };\n}\n\n/**\n * Sigmoid confidence calibration.\n * Maps distance from tier boundary to [0.5, 1.0] confidence range.\n */\nfunction calibrateConfidence(distance: number, steepness: number): number {\n return 1 / (1 + Math.exp(-steepness * distance));\n}\n","/**\n * Tier → Model Selection\n *\n * Maps a classification tier to the cheapest capable model.\n * Builds RoutingDecision metadata with cost estimates and savings.\n */\n\nimport type { Tier, TierConfig, RoutingDecision } from \"./types.js\";\n\nexport type ModelPricing = {\n inputPrice: number; // per 1M tokens\n outputPrice: number; // per 1M tokens\n};\n\n/**\n * Select the primary model for a tier and build the RoutingDecision.\n */\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n): RoutingDecision {\n const tierConfig = tierConfigs[tier];\n const model = tierConfig.primary;\n const pricing = modelPricing.get(model);\n\n // Defensive: guard against undefined price fields (not just undefined pricing)\n const inputPrice = pricing?.inputPrice ?? 0;\n const outputPrice = pricing?.outputPrice ?? 0;\n const inputCost = (estimatedInputTokens / 1_000_000) * inputPrice;\n const outputCost = (maxOutputTokens / 1_000_000) * outputPrice;\n const costEstimate = inputCost + outputCost;\n\n // Baseline: what Claude Opus would cost (the premium default)\n const opusPricing = modelPricing.get(\"anthropic/claude-opus-4\");\n const opusInputPrice = opusPricing?.inputPrice ?? 0;\n const opusOutputPrice = opusPricing?.outputPrice ?? 0;\n const baselineInput = (estimatedInputTokens / 1_000_000) * opusInputPrice;\n const baselineOutput = (maxOutputTokens / 1_000_000) * opusOutputPrice;\n const baselineCost = baselineInput + baselineOutput;\n\n const savings = baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;\n\n return {\n model,\n tier,\n confidence,\n method,\n reasoning,\n costEstimate,\n baselineCost,\n savings,\n };\n}\n\n/**\n * Get the ordered fallback chain for a tier: [primary, ...fallbacks].\n */\nexport function getFallbackChain(tier: Tier, tierConfigs: Record<Tier, TierConfig>): string[] {\n const config = tierConfigs[tier];\n return [config.primary, ...config.fallback];\n}\n\n/**\n * Calculate cost for a specific model (used when fallback model is used).\n * Returns updated cost fields for RoutingDecision.\n */\nexport function calculateModelCost(\n model: string,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n): { costEstimate: number; baselineCost: number; savings: number } {\n const pricing = modelPricing.get(model);\n\n // Defensive: guard against undefined price fields (not just undefined pricing)\n const inputPrice = pricing?.inputPrice ?? 0;\n const outputPrice = pricing?.outputPrice ?? 0;\n const inputCost = (estimatedInputTokens / 1_000_000) * inputPrice;\n const outputCost = (maxOutputTokens / 1_000_000) * outputPrice;\n const costEstimate = inputCost + outputCost;\n\n // Baseline: what Claude Opus would cost\n const opusPricing = modelPricing.get(\"anthropic/claude-opus-4\");\n const opusInputPrice = opusPricing?.inputPrice ?? 0;\n const opusOutputPrice = opusPricing?.outputPrice ?? 0;\n const baselineInput = (estimatedInputTokens / 1_000_000) * opusInputPrice;\n const baselineOutput = (maxOutputTokens / 1_000_000) * opusOutputPrice;\n const baselineCost = baselineInput + baselineOutput;\n\n const savings = baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;\n\n return { costEstimate, baselineCost, savings };\n}\n\n/**\n * Get the fallback chain filtered by context length.\n * Only returns models that can handle the estimated total context.\n *\n * @param tier - The tier to get fallback chain for\n * @param tierConfigs - Tier configurations\n * @param estimatedTotalTokens - Estimated total context (input + output)\n * @param getContextWindow - Function to get context window for a model ID\n * @returns Filtered list of models that can handle the context\n */\nexport function getFallbackChainFiltered(\n tier: Tier,\n tierConfigs: Record<Tier, TierConfig>,\n estimatedTotalTokens: number,\n getContextWindow: (modelId: string) => number | undefined,\n): string[] {\n const fullChain = getFallbackChain(tier, tierConfigs);\n\n // Filter to models that can handle the context\n const filtered = fullChain.filter((modelId) => {\n const contextWindow = getContextWindow(modelId);\n if (contextWindow === undefined) {\n // Unknown model - include it (let API reject if needed)\n return true;\n }\n // Add 10% buffer for safety\n return contextWindow >= estimatedTotalTokens * 1.1;\n });\n\n // If all models filtered out, return the original chain\n // (let the API error out - better than no options)\n if (filtered.length === 0) {\n return fullChain;\n }\n\n return filtered;\n}\n","/**\n * Default Routing Config\n *\n * All routing parameters as a TypeScript constant.\n * Operators override via openclaw.yaml plugin config.\n *\n * Scoring uses 14 weighted dimensions with sigmoid confidence calibration.\n */\n\nimport type { RoutingConfig } from \"./types.js\";\n\nexport const DEFAULT_ROUTING_CONFIG: RoutingConfig = {\n version: \"2.0\",\n\n classifier: {\n llmModel: \"google/gemini-2.5-flash\",\n llmMaxTokens: 10,\n llmTemperature: 0,\n promptTruncationChars: 500,\n cacheTtlMs: 3_600_000, // 1 hour\n },\n\n scoring: {\n tokenCountThresholds: { simple: 50, complex: 500 },\n\n // Multilingual keywords: English + Chinese (中文) + Japanese (日本語) + Russian (Русский) + German (Deutsch)\n codeKeywords: [\n // English\n \"function\",\n \"class\",\n \"import\",\n \"def\",\n \"SELECT\",\n \"async\",\n \"await\",\n \"const\",\n \"let\",\n \"var\",\n \"return\",\n \"```\",\n // Chinese\n \"函数\",\n \"类\",\n \"导入\",\n \"定义\",\n \"查询\",\n \"异步\",\n \"等待\",\n \"常量\",\n \"变量\",\n \"返回\",\n // Japanese\n \"関数\",\n \"クラス\",\n \"インポート\",\n \"非同期\",\n \"定数\",\n \"変数\",\n // Russian\n \"функция\",\n \"класс\",\n \"импорт\",\n \"определ\",\n \"запрос\",\n \"асинхронный\",\n \"ожидать\",\n \"константа\",\n \"переменная\",\n \"вернуть\",\n // German\n \"funktion\",\n \"klasse\",\n \"importieren\",\n \"definieren\",\n \"abfrage\",\n \"asynchron\",\n \"erwarten\",\n \"konstante\",\n \"variable\",\n \"zurückgeben\",\n ],\n reasoningKeywords: [\n // English\n \"prove\",\n \"theorem\",\n \"derive\",\n \"step by step\",\n \"chain of thought\",\n \"formally\",\n \"mathematical\",\n \"proof\",\n \"logically\",\n // Chinese\n \"证明\",\n \"定理\",\n \"推导\",\n \"逐步\",\n \"思维链\",\n \"形式化\",\n \"数学\",\n \"逻辑\",\n // Japanese\n \"証明\",\n \"定理\",\n \"導出\",\n \"ステップバイステップ\",\n \"論理的\",\n // Russian\n \"доказать\",\n \"докажи\",\n \"доказательств\",\n \"теорема\",\n \"вывести\",\n \"шаг за шагом\",\n \"пошагово\",\n \"поэтапно\",\n \"цепочка рассуждений\",\n \"рассуждени\",\n \"формально\",\n \"математически\",\n \"логически\",\n // German\n \"beweisen\",\n \"beweis\",\n \"theorem\",\n \"ableiten\",\n \"schritt für schritt\",\n \"gedankenkette\",\n \"formal\",\n \"mathematisch\",\n \"logisch\",\n ],\n simpleKeywords: [\n // English\n \"what is\",\n \"define\",\n \"translate\",\n \"hello\",\n \"yes or no\",\n \"capital of\",\n \"how old\",\n \"who is\",\n \"when was\",\n // Chinese\n \"什么是\",\n \"定义\",\n \"翻译\",\n \"你好\",\n \"是否\",\n \"首都\",\n \"多大\",\n \"谁是\",\n \"何时\",\n // Japanese\n \"とは\",\n \"定義\",\n \"翻訳\",\n \"こんにちは\",\n \"はいかいいえ\",\n \"首都\",\n \"誰\",\n // Russian\n \"что такое\",\n \"определение\",\n \"перевести\",\n \"переведи\",\n \"привет\",\n \"да или нет\",\n \"столица\",\n \"сколько лет\",\n \"кто такой\",\n \"когда\",\n \"объясни\",\n // German\n \"was ist\",\n \"definiere\",\n \"übersetze\",\n \"hallo\",\n \"ja oder nein\",\n \"hauptstadt\",\n \"wie alt\",\n \"wer ist\",\n \"wann\",\n \"erkläre\",\n ],\n technicalKeywords: [\n // English\n \"algorithm\",\n \"optimize\",\n \"architecture\",\n \"distributed\",\n \"kubernetes\",\n \"microservice\",\n \"database\",\n \"infrastructure\",\n // Chinese\n \"算法\",\n \"优化\",\n \"架构\",\n \"分布式\",\n \"微服务\",\n \"数据库\",\n \"基础设施\",\n // Japanese\n \"アルゴリズム\",\n \"最適化\",\n \"アーキテクチャ\",\n \"分散\",\n \"マイクロサービス\",\n \"データベース\",\n // Russian\n \"алгоритм\",\n \"оптимизировать\",\n \"оптимизаци\",\n \"оптимизируй\",\n \"архитектура\",\n \"распределённый\",\n \"микросервис\",\n \"база данных\",\n \"инфраструктура\",\n // German\n \"algorithmus\",\n \"optimieren\",\n \"architektur\",\n \"verteilt\",\n \"kubernetes\",\n \"mikroservice\",\n \"datenbank\",\n \"infrastruktur\",\n ],\n creativeKeywords: [\n // English\n \"story\",\n \"poem\",\n \"compose\",\n \"brainstorm\",\n \"creative\",\n \"imagine\",\n \"write a\",\n // Chinese\n \"故事\",\n \"诗\",\n \"创作\",\n \"头脑风暴\",\n \"创意\",\n \"想象\",\n \"写一个\",\n // Japanese\n \"物語\",\n \"詩\",\n \"作曲\",\n \"ブレインストーム\",\n \"創造的\",\n \"想像\",\n // Russian\n \"история\",\n \"рассказ\",\n \"стихотворение\",\n \"сочинить\",\n \"сочини\",\n \"мозговой штурм\",\n \"творческий\",\n \"представить\",\n \"придумай\",\n \"напиши\",\n // German\n \"geschichte\",\n \"gedicht\",\n \"komponieren\",\n \"brainstorming\",\n \"kreativ\",\n \"vorstellen\",\n \"schreibe\",\n \"erzählung\",\n ],\n\n // New dimension keyword lists (multilingual)\n imperativeVerbs: [\n // English\n \"build\",\n \"create\",\n \"implement\",\n \"design\",\n \"develop\",\n \"construct\",\n \"generate\",\n \"deploy\",\n \"configure\",\n \"set up\",\n // Chinese\n \"构建\",\n \"创建\",\n \"实现\",\n \"设计\",\n \"开发\",\n \"生成\",\n \"部署\",\n \"配置\",\n \"设置\",\n // Japanese\n \"構築\",\n \"作成\",\n \"実装\",\n \"設計\",\n \"開発\",\n \"生成\",\n \"デプロイ\",\n \"設定\",\n // Russian\n \"построить\",\n \"построй\",\n \"создать\",\n \"создай\",\n \"реализовать\",\n \"реализуй\",\n \"спроектировать\",\n \"разработать\",\n \"разработай\",\n \"сконструировать\",\n \"сгенерировать\",\n \"сгенерируй\",\n \"развернуть\",\n \"разверни\",\n \"настроить\",\n \"настрой\",\n // German\n \"erstellen\",\n \"bauen\",\n \"implementieren\",\n \"entwerfen\",\n \"entwickeln\",\n \"konstruieren\",\n \"generieren\",\n \"bereitstellen\",\n \"konfigurieren\",\n \"einrichten\",\n ],\n constraintIndicators: [\n // English\n \"under\",\n \"at most\",\n \"at least\",\n \"within\",\n \"no more than\",\n \"o(\",\n \"maximum\",\n \"minimum\",\n \"limit\",\n \"budget\",\n // Chinese\n \"不超过\",\n \"至少\",\n \"最多\",\n \"在内\",\n \"最大\",\n \"最小\",\n \"限制\",\n \"预算\",\n // Japanese\n \"以下\",\n \"最大\",\n \"最小\",\n \"制限\",\n \"予算\",\n // Russian\n \"не более\",\n \"не менее\",\n \"как минимум\",\n \"в пределах\",\n \"максимум\",\n \"минимум\",\n \"ограничение\",\n \"бюджет\",\n // German\n \"höchstens\",\n \"mindestens\",\n \"innerhalb\",\n \"nicht mehr als\",\n \"maximal\",\n \"minimal\",\n \"grenze\",\n \"budget\",\n ],\n outputFormatKeywords: [\n // English\n \"json\",\n \"yaml\",\n \"xml\",\n \"table\",\n \"csv\",\n \"markdown\",\n \"schema\",\n \"format as\",\n \"structured\",\n // Chinese\n \"表格\",\n \"格式化为\",\n \"结构化\",\n // Japanese\n \"テーブル\",\n \"フォーマット\",\n \"構造化\",\n // Russian\n \"таблица\",\n \"форматировать как\",\n \"структурированный\",\n // German\n \"tabelle\",\n \"formatieren als\",\n \"strukturiert\",\n ],\n referenceKeywords: [\n // English\n \"above\",\n \"below\",\n \"previous\",\n \"following\",\n \"the docs\",\n \"the api\",\n \"the code\",\n \"earlier\",\n \"attached\",\n // Chinese\n \"上面\",\n \"下面\",\n \"之前\",\n \"接下来\",\n \"文档\",\n \"代码\",\n \"附件\",\n // Japanese\n \"上記\",\n \"下記\",\n \"前の\",\n \"次の\",\n \"ドキュメント\",\n \"コード\",\n // Russian\n \"выше\",\n \"ниже\",\n \"предыдущий\",\n \"следующий\",\n \"документация\",\n \"код\",\n \"ранее\",\n \"вложение\",\n // German\n \"oben\",\n \"unten\",\n \"vorherige\",\n \"folgende\",\n \"dokumentation\",\n \"der code\",\n \"früher\",\n \"anhang\",\n ],\n negationKeywords: [\n // English\n \"don't\",\n \"do not\",\n \"avoid\",\n \"never\",\n \"without\",\n \"except\",\n \"exclude\",\n \"no longer\",\n // Chinese\n \"不要\",\n \"避免\",\n \"从不\",\n \"没有\",\n \"除了\",\n \"排除\",\n // Japanese\n \"しないで\",\n \"避ける\",\n \"決して\",\n \"なしで\",\n \"除く\",\n // Russian\n \"не делай\",\n \"не надо\",\n \"нельзя\",\n \"избегать\",\n \"никогда\",\n \"без\",\n \"кроме\",\n \"исключить\",\n \"больше не\",\n // German\n \"nicht\",\n \"vermeide\",\n \"niemals\",\n \"ohne\",\n \"außer\",\n \"ausschließen\",\n \"nicht mehr\",\n ],\n domainSpecificKeywords: [\n // English\n \"quantum\",\n \"fpga\",\n \"vlsi\",\n \"risc-v\",\n \"asic\",\n \"photonics\",\n \"genomics\",\n \"proteomics\",\n \"topological\",\n \"homomorphic\",\n \"zero-knowledge\",\n \"lattice-based\",\n // Chinese\n \"量子\",\n \"光子学\",\n \"基因组学\",\n \"蛋白质组学\",\n \"拓扑\",\n \"同态\",\n \"零知识\",\n \"格密码\",\n // Japanese\n \"量子\",\n \"フォトニクス\",\n \"ゲノミクス\",\n \"トポロジカル\",\n // Russian\n \"квантовый\",\n \"фотоника\",\n \"геномика\",\n \"протеомика\",\n \"топологический\",\n \"гомоморфный\",\n \"с нулевым разглашением\",\n \"на основе решёток\",\n // German\n \"quanten\",\n \"photonik\",\n \"genomik\",\n \"proteomik\",\n \"topologisch\",\n \"homomorph\",\n \"zero-knowledge\",\n \"gitterbasiert\",\n ],\n\n // Agentic task keywords - file ops, execution, multi-step, iterative work\n // Pruned: removed overly common words like \"then\", \"first\", \"run\", \"test\", \"build\"\n agenticTaskKeywords: [\n // English - File operations (clearly agentic)\n \"read file\",\n \"read the file\",\n \"look at\",\n \"check the\",\n \"open the\",\n \"edit\",\n \"modify\",\n \"update the\",\n \"change the\",\n \"write to\",\n \"create file\",\n // English - Execution (specific commands only)\n \"execute\",\n \"deploy\",\n \"install\",\n \"npm\",\n \"pip\",\n \"compile\",\n // English - Multi-step patterns (specific only)\n \"after that\",\n \"and also\",\n \"once done\",\n \"step 1\",\n \"step 2\",\n // English - Iterative work\n \"fix\",\n \"debug\",\n \"until it works\",\n \"keep trying\",\n \"iterate\",\n \"make sure\",\n \"verify\",\n \"confirm\",\n // Chinese (keep specific ones)\n \"读取文件\",\n \"查看\",\n \"打开\",\n \"编辑\",\n \"修改\",\n \"更新\",\n \"创建\",\n \"执行\",\n \"部署\",\n \"安装\",\n \"第一步\",\n \"第二步\",\n \"修复\",\n \"调试\",\n \"直到\",\n \"确认\",\n \"验证\",\n ],\n\n // Dimension weights (sum to 1.0)\n dimensionWeights: {\n tokenCount: 0.08,\n codePresence: 0.15,\n reasoningMarkers: 0.18,\n technicalTerms: 0.1,\n creativeMarkers: 0.05,\n simpleIndicators: 0.02, // Reduced from 0.12 to make room for agenticTask\n multiStepPatterns: 0.12,\n questionComplexity: 0.05,\n imperativeVerbs: 0.03,\n constraintCount: 0.04,\n outputFormat: 0.03,\n referenceComplexity: 0.02,\n negationComplexity: 0.01,\n domainSpecificity: 0.02,\n agenticTask: 0.04, // Reduced - agentic signals influence tier selection, not dominate it\n },\n\n // Tier boundaries on weighted score axis\n tierBoundaries: {\n simpleMedium: 0.0,\n mediumComplex: 0.18,\n complexReasoning: 0.4, // Raised from 0.25 - requires strong reasoning signals\n },\n\n // Sigmoid steepness for confidence calibration\n confidenceSteepness: 12,\n // Below this confidence → ambiguous (null tier)\n confidenceThreshold: 0.7,\n },\n\n tiers: {\n SIMPLE: {\n primary: \"google/gemini-2.5-flash\",\n fallback: [\"nvidia/gpt-oss-120b\", \"deepseek/deepseek-chat\", \"openai/gpt-4o-mini\"],\n },\n MEDIUM: {\n primary: \"xai/grok-code-fast-1\", // Code specialist, $0.20/$1.50\n fallback: [\n \"deepseek/deepseek-chat\",\n \"xai/grok-4-fast-non-reasoning\",\n \"google/gemini-2.5-flash\",\n ],\n },\n COMPLEX: {\n primary: \"google/gemini-2.5-pro\",\n fallback: [\"anthropic/claude-sonnet-4\", \"xai/grok-4-0709\", \"openai/gpt-4o\"],\n },\n REASONING: {\n primary: \"xai/grok-4-fast-reasoning\", // Ultra-cheap reasoning $0.20/$0.50\n fallback: [\"deepseek/deepseek-reasoner\", \"moonshot/kimi-k2.5\", \"google/gemini-2.5-pro\"],\n },\n },\n\n // Agentic tier configs - models that excel at multi-step autonomous tasks\n agenticTiers: {\n SIMPLE: {\n primary: \"moonshot/kimi-k2.5\", // Cheaper than Haiku ($0.5/$2.4 vs $1/$5), larger context\n fallback: [\n \"anthropic/claude-haiku-4.5\",\n \"xai/grok-4-fast-non-reasoning\",\n \"openai/gpt-4o-mini\",\n ],\n },\n MEDIUM: {\n primary: \"xai/grok-code-fast-1\", // Code specialist for agentic coding\n fallback: [\"moonshot/kimi-k2.5\", \"anthropic/claude-haiku-4.5\", \"anthropic/claude-sonnet-4\"],\n },\n COMPLEX: {\n primary: \"anthropic/claude-sonnet-4\",\n fallback: [\"anthropic/claude-opus-4\", \"xai/grok-4-0709\", \"openai/gpt-4o\"],\n },\n REASONING: {\n primary: \"anthropic/claude-sonnet-4\", // Strong tool use + reasoning for agentic tasks\n fallback: [\"xai/grok-4-fast-reasoning\", \"moonshot/kimi-k2.5\", \"deepseek/deepseek-reasoner\"],\n },\n },\n\n overrides: {\n maxTokensForceComplex: 100_000,\n structuredOutputMinTier: \"MEDIUM\",\n ambiguousDefaultTier: \"MEDIUM\",\n agenticMode: false,\n },\n};\n","/**\n * Smart Router Entry Point\n *\n * Classifies requests and routes to the cheapest capable model.\n * 100% local — rules-based scoring handles all requests in <1ms.\n * Ambiguous cases default to configurable tier (MEDIUM by default).\n */\n\nimport type { Tier, RoutingDecision, RoutingConfig } from \"./types.js\";\nimport { classifyByRules } from \"./rules.js\";\nimport { selectModel, type ModelPricing } from \"./selector.js\";\n\nexport type RouterOptions = {\n config: RoutingConfig;\n modelPricing: Map<string, ModelPricing>;\n};\n\n/**\n * Route a request to the cheapest capable model.\n *\n * 1. Check overrides (large context, structured output)\n * 2. Run rule-based classifier (14 weighted dimensions, <1ms)\n * 3. If ambiguous, default to configurable tier (no external API calls)\n * 4. Select model for tier\n * 5. Return RoutingDecision with metadata\n */\nexport function route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n): RoutingDecision {\n const { config, modelPricing } = options;\n\n // Estimate input tokens (~4 chars per token)\n const fullText = `${systemPrompt ?? \"\"} ${prompt}`;\n const estimatedTokens = Math.ceil(fullText.length / 4);\n\n // --- Rule-based classification (runs first to get agenticScore) ---\n const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);\n\n // Determine if agentic tiers should be used:\n // 1. Explicit agenticMode config OR\n // 2. Auto-detected agentic task (agenticScore >= 0.69)\n const agenticScore = ruleResult.agenticScore ?? 0;\n const isAutoAgentic = agenticScore >= 0.69;\n const isExplicitAgentic = config.overrides.agenticMode ?? false;\n const useAgenticTiers = (isAutoAgentic || isExplicitAgentic) && config.agenticTiers != null;\n const tierConfigs = useAgenticTiers ? config.agenticTiers! : config.tiers;\n\n // --- Override: large context → force COMPLEX ---\n if (estimatedTokens > config.overrides.maxTokensForceComplex) {\n return selectModel(\n \"COMPLEX\",\n 0.95,\n \"rules\",\n `Input exceeds ${config.overrides.maxTokensForceComplex} tokens${useAgenticTiers ? \" | agentic\" : \"\"}`,\n tierConfigs,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n }\n\n // Structured output detection\n const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;\n\n let tier: Tier;\n let confidence: number;\n const method: \"rules\" | \"llm\" = \"rules\";\n let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(\", \")}`;\n\n if (ruleResult.tier !== null) {\n tier = ruleResult.tier;\n confidence = ruleResult.confidence;\n } else {\n // Ambiguous — default to configurable tier (no external API call)\n tier = config.overrides.ambiguousDefaultTier;\n confidence = 0.5;\n reasoning += ` | ambiguous -> default: ${tier}`;\n }\n\n // Apply structured output minimum tier\n if (hasStructuredOutput) {\n const tierRank: Record<Tier, number> = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };\n const minTier = config.overrides.structuredOutputMinTier;\n if (tierRank[tier] < tierRank[minTier]) {\n reasoning += ` | upgraded to ${minTier} (structured output)`;\n tier = minTier;\n }\n }\n\n // Add agentic mode indicator to reasoning\n if (isAutoAgentic) {\n reasoning += \" | auto-agentic\";\n } else if (isExplicitAgentic) {\n reasoning += \" | agentic\";\n }\n\n return selectModel(\n tier,\n confidence,\n method,\n reasoning,\n tierConfigs,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n}\n\nexport { getFallbackChain, getFallbackChainFiltered, calculateModelCost } from \"./selector.js\";\nexport { DEFAULT_ROUTING_CONFIG } from \"./config.js\";\nexport type { RoutingDecision, Tier, RoutingConfig } from \"./types.js\";\nexport type { ModelPricing } from \"./selector.js\";\n","/**\n * BlockRun Model Definitions for OpenClaw\n *\n * Maps BlockRun's 30+ AI models to OpenClaw's ModelDefinitionConfig format.\n * All models use the \"openai-completions\" API since BlockRun is OpenAI-compatible.\n *\n * Pricing is in USD per 1M tokens. Operators pay these rates via x402;\n * they set their own markup when reselling to end users (Phase 2).\n */\n\nimport type { ModelDefinitionConfig, ModelProviderConfig } from \"./types.js\";\n\n/**\n * Model aliases for convenient shorthand access.\n * Users can type `/model claude` instead of `/model blockrun/anthropic/claude-sonnet-4`.\n */\nexport const MODEL_ALIASES: Record<string, string> = {\n // Claude\n claude: \"anthropic/claude-sonnet-4\",\n sonnet: \"anthropic/claude-sonnet-4\",\n opus: \"anthropic/claude-opus-4\",\n haiku: \"anthropic/claude-haiku-4.5\",\n\n // OpenAI\n gpt: \"openai/gpt-4o\",\n gpt4: \"openai/gpt-4o\",\n gpt5: \"openai/gpt-5.2\",\n mini: \"openai/gpt-4o-mini\",\n o3: \"openai/o3\",\n\n // DeepSeek\n deepseek: \"deepseek/deepseek-chat\",\n reasoner: \"deepseek/deepseek-reasoner\",\n\n // Kimi / Moonshot\n kimi: \"moonshot/kimi-k2.5\",\n\n // Google\n gemini: \"google/gemini-2.5-pro\",\n flash: \"google/gemini-2.5-flash\",\n\n // xAI\n grok: \"xai/grok-3\",\n \"grok-fast\": \"xai/grok-4-fast-reasoning\",\n \"grok-code\": \"xai/grok-code-fast-1\",\n\n // NVIDIA (free)\n nvidia: \"nvidia/gpt-oss-120b\",\n \"gpt-120b\": \"nvidia/gpt-oss-120b\",\n \"gpt-20b\": \"nvidia/gpt-oss-20b\",\n free: \"nvidia/gpt-oss-120b\",\n};\n\n/**\n * Resolve a model alias to its full model ID.\n * Returns the original model if not an alias.\n */\nexport function resolveModelAlias(model: string): string {\n const normalized = model.trim().toLowerCase();\n const resolved = MODEL_ALIASES[normalized];\n if (resolved) return resolved;\n\n // Check with \"blockrun/\" prefix stripped\n if (normalized.startsWith(\"blockrun/\")) {\n const withoutPrefix = normalized.slice(\"blockrun/\".length);\n const resolvedWithoutPrefix = MODEL_ALIASES[withoutPrefix];\n if (resolvedWithoutPrefix) return resolvedWithoutPrefix;\n }\n\n return model;\n}\n\ntype BlockRunModel = {\n id: string;\n name: string;\n inputPrice: number;\n outputPrice: number;\n contextWindow: number;\n maxOutput: number;\n reasoning?: boolean;\n vision?: boolean;\n /** Models optimized for agentic workflows (multi-step autonomous tasks) */\n agentic?: boolean;\n};\n\nexport const BLOCKRUN_MODELS: BlockRunModel[] = [\n // Smart routing meta-model — proxy replaces with actual model\n // NOTE: Model IDs are WITHOUT provider prefix (OpenClaw adds \"blockrun/\" automatically)\n {\n id: \"auto\",\n name: \"BlockRun Smart Router\",\n inputPrice: 0,\n outputPrice: 0,\n contextWindow: 1_050_000,\n maxOutput: 128_000,\n },\n\n // OpenAI GPT-5 Family\n {\n id: \"openai/gpt-5.2\",\n name: \"GPT-5.2\",\n inputPrice: 1.75,\n outputPrice: 14.0,\n contextWindow: 400000,\n maxOutput: 128000,\n reasoning: true,\n vision: true,\n agentic: true,\n },\n {\n id: \"openai/gpt-5-mini\",\n name: \"GPT-5 Mini\",\n inputPrice: 0.25,\n outputPrice: 2.0,\n contextWindow: 200000,\n maxOutput: 65536,\n },\n {\n id: \"openai/gpt-5-nano\",\n name: \"GPT-5 Nano\",\n inputPrice: 0.05,\n outputPrice: 0.4,\n contextWindow: 128000,\n maxOutput: 32768,\n },\n {\n id: \"openai/gpt-5.2-pro\",\n name: \"GPT-5.2 Pro\",\n inputPrice: 21.0,\n outputPrice: 168.0,\n contextWindow: 400000,\n maxOutput: 128000,\n reasoning: true,\n },\n\n // OpenAI GPT-4 Family\n {\n id: \"openai/gpt-4.1\",\n name: \"GPT-4.1\",\n inputPrice: 2.0,\n outputPrice: 8.0,\n contextWindow: 128000,\n maxOutput: 16384,\n vision: true,\n },\n {\n id: \"openai/gpt-4.1-mini\",\n name: \"GPT-4.1 Mini\",\n inputPrice: 0.4,\n outputPrice: 1.6,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"openai/gpt-4.1-nano\",\n name: \"GPT-4.1 Nano\",\n inputPrice: 0.1,\n outputPrice: 0.4,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"openai/gpt-4o\",\n name: \"GPT-4o\",\n inputPrice: 2.5,\n outputPrice: 10.0,\n contextWindow: 128000,\n maxOutput: 16384,\n vision: true,\n agentic: true,\n },\n {\n id: \"openai/gpt-4o-mini\",\n name: \"GPT-4o Mini\",\n inputPrice: 0.15,\n outputPrice: 0.6,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n\n // OpenAI O-series (Reasoning)\n {\n id: \"openai/o1\",\n name: \"o1\",\n inputPrice: 15.0,\n outputPrice: 60.0,\n contextWindow: 200000,\n maxOutput: 100000,\n reasoning: true,\n },\n {\n id: \"openai/o1-mini\",\n name: \"o1-mini\",\n inputPrice: 1.1,\n outputPrice: 4.4,\n contextWindow: 128000,\n maxOutput: 65536,\n reasoning: true,\n },\n {\n id: \"openai/o3\",\n name: \"o3\",\n inputPrice: 2.0,\n outputPrice: 8.0,\n contextWindow: 200000,\n maxOutput: 100000,\n reasoning: true,\n },\n {\n id: \"openai/o3-mini\",\n name: \"o3-mini\",\n inputPrice: 1.1,\n outputPrice: 4.4,\n contextWindow: 128000,\n maxOutput: 65536,\n reasoning: true,\n },\n {\n id: \"openai/o4-mini\",\n name: \"o4-mini\",\n inputPrice: 1.1,\n outputPrice: 4.4,\n contextWindow: 128000,\n maxOutput: 65536,\n reasoning: true,\n },\n\n // Anthropic - all Claude models excel at agentic workflows\n {\n id: \"anthropic/claude-haiku-4.5\",\n name: \"Claude Haiku 4.5\",\n inputPrice: 1.0,\n outputPrice: 5.0,\n contextWindow: 200000,\n maxOutput: 8192,\n agentic: true,\n },\n {\n id: \"anthropic/claude-sonnet-4\",\n name: \"Claude Sonnet 4\",\n inputPrice: 3.0,\n outputPrice: 15.0,\n contextWindow: 200000,\n maxOutput: 64000,\n reasoning: true,\n agentic: true,\n },\n {\n id: \"anthropic/claude-opus-4\",\n name: \"Claude Opus 4\",\n inputPrice: 15.0,\n outputPrice: 75.0,\n contextWindow: 200000,\n maxOutput: 32000,\n reasoning: true,\n agentic: true,\n },\n {\n id: \"anthropic/claude-opus-4.5\",\n name: \"Claude Opus 4.5\",\n inputPrice: 5.0,\n outputPrice: 25.0,\n contextWindow: 200000,\n maxOutput: 32000,\n reasoning: true,\n agentic: true,\n },\n\n // Google\n {\n id: \"google/gemini-3-pro-preview\",\n name: \"Gemini 3 Pro Preview\",\n inputPrice: 2.0,\n outputPrice: 12.0,\n contextWindow: 1050000,\n maxOutput: 65536,\n reasoning: true,\n vision: true,\n },\n {\n id: \"google/gemini-2.5-pro\",\n name: \"Gemini 2.5 Pro\",\n inputPrice: 1.25,\n outputPrice: 10.0,\n contextWindow: 1050000,\n maxOutput: 65536,\n reasoning: true,\n vision: true,\n },\n {\n id: \"google/gemini-2.5-flash\",\n name: \"Gemini 2.5 Flash\",\n inputPrice: 0.15,\n outputPrice: 0.6,\n contextWindow: 1000000,\n maxOutput: 65536,\n },\n\n // DeepSeek\n {\n id: \"deepseek/deepseek-chat\",\n name: \"DeepSeek V3.2 Chat\",\n inputPrice: 0.28,\n outputPrice: 0.42,\n contextWindow: 128000,\n maxOutput: 8192,\n },\n {\n id: \"deepseek/deepseek-reasoner\",\n name: \"DeepSeek V3.2 Reasoner\",\n inputPrice: 0.28,\n outputPrice: 0.42,\n contextWindow: 128000,\n maxOutput: 8192,\n reasoning: true,\n },\n\n // Moonshot / Kimi - optimized for agentic workflows\n {\n id: \"moonshot/kimi-k2.5\",\n name: \"Kimi K2.5\",\n inputPrice: 0.5,\n outputPrice: 2.4,\n contextWindow: 262144,\n maxOutput: 8192,\n reasoning: true,\n vision: true,\n agentic: true,\n },\n\n // xAI / Grok\n {\n id: \"xai/grok-3\",\n name: \"Grok 3\",\n inputPrice: 3.0,\n outputPrice: 15.0,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-3-fast\",\n name: \"Grok 3 Fast\",\n inputPrice: 5.0,\n outputPrice: 25.0,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-3-mini\",\n name: \"Grok 3 Mini\",\n inputPrice: 0.3,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n },\n\n // xAI Grok 4 Family - Ultra-cheap fast models\n {\n id: \"xai/grok-4-fast-reasoning\",\n name: \"Grok 4 Fast Reasoning\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-4-fast-non-reasoning\",\n name: \"Grok 4 Fast\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n },\n {\n id: \"xai/grok-4-1-fast-reasoning\",\n name: \"Grok 4.1 Fast Reasoning\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-4-1-fast-non-reasoning\",\n name: \"Grok 4.1 Fast\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n },\n {\n id: \"xai/grok-code-fast-1\",\n name: \"Grok Code Fast\",\n inputPrice: 0.2,\n outputPrice: 1.5,\n contextWindow: 131072,\n maxOutput: 16384,\n agentic: true, // Good for coding tasks\n },\n {\n id: \"xai/grok-4-0709\",\n name: \"Grok 4 (0709)\",\n inputPrice: 3.0,\n outputPrice: 15.0,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-2-vision\",\n name: \"Grok 2 Vision\",\n inputPrice: 2.0,\n outputPrice: 10.0,\n contextWindow: 131072,\n maxOutput: 16384,\n vision: true,\n },\n\n // NVIDIA - Free/cheap models\n {\n id: \"nvidia/gpt-oss-120b\",\n name: \"NVIDIA GPT-OSS 120B\",\n inputPrice: 0,\n outputPrice: 0,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"nvidia/gpt-oss-20b\",\n name: \"NVIDIA GPT-OSS 20B\",\n inputPrice: 0,\n outputPrice: 0,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"nvidia/kimi-k2.5\",\n name: \"NVIDIA Kimi K2.5\",\n inputPrice: 0.001,\n outputPrice: 0.001,\n contextWindow: 262144,\n maxOutput: 16384,\n },\n];\n\n/**\n * Convert BlockRun model definitions to OpenClaw ModelDefinitionConfig format.\n */\nfunction toOpenClawModel(m: BlockRunModel): ModelDefinitionConfig {\n return {\n id: m.id,\n name: m.name,\n api: \"openai-completions\",\n reasoning: m.reasoning ?? false,\n input: m.vision ? [\"text\", \"image\"] : [\"text\"],\n cost: {\n input: m.inputPrice,\n output: m.outputPrice,\n cacheRead: 0,\n cacheWrite: 0,\n },\n contextWindow: m.contextWindow,\n maxTokens: m.maxOutput,\n };\n}\n\n/**\n * Alias models that map to real models.\n * These allow users to use friendly names like \"free\" or \"gpt-120b\".\n */\nconst ALIAS_MODELS: ModelDefinitionConfig[] = Object.entries(MODEL_ALIASES)\n .map(([alias, targetId]) => {\n const target = BLOCKRUN_MODELS.find((m) => m.id === targetId);\n if (!target) return null;\n return toOpenClawModel({ ...target, id: alias, name: `${alias} → ${target.name}` });\n })\n .filter((m): m is ModelDefinitionConfig => m !== null);\n\n/**\n * All BlockRun models in OpenClaw format (including aliases).\n */\nexport const OPENCLAW_MODELS: ModelDefinitionConfig[] = [\n ...BLOCKRUN_MODELS.map(toOpenClawModel),\n ...ALIAS_MODELS,\n];\n\n/**\n * Build a ModelProviderConfig for BlockRun.\n *\n * @param baseUrl - The proxy's local base URL (e.g., \"http://127.0.0.1:12345\")\n */\nexport function buildProviderModels(baseUrl: string): ModelProviderConfig {\n return {\n baseUrl: `${baseUrl}/v1`,\n api: \"openai-completions\",\n models: OPENCLAW_MODELS,\n };\n}\n\n/**\n * Check if a model is optimized for agentic workflows.\n * Agentic models continue autonomously with multi-step tasks\n * instead of stopping and waiting for user input.\n */\nexport function isAgenticModel(modelId: string): boolean {\n const model = BLOCKRUN_MODELS.find(\n (m) => m.id === modelId || m.id === modelId.replace(\"blockrun/\", \"\"),\n );\n return model?.agentic ?? false;\n}\n\n/**\n * Get all agentic-capable models.\n */\nexport function getAgenticModels(): string[] {\n return BLOCKRUN_MODELS.filter((m) => m.agentic).map((m) => m.id);\n}\n\n/**\n * Get context window size for a model.\n * Returns undefined if model not found.\n */\nexport function getModelContextWindow(modelId: string): number | undefined {\n const normalized = modelId.replace(\"blockrun/\", \"\");\n const model = BLOCKRUN_MODELS.find((m) => m.id === normalized);\n return model?.contextWindow;\n}\n\n/**\n * Check if a model has reasoning/thinking capabilities.\n * Reasoning models may require reasoning_content in assistant tool_call messages.\n */\nexport function isReasoningModel(modelId: string): boolean {\n const normalized = modelId.replace(\"blockrun/\", \"\");\n const model = BLOCKRUN_MODELS.find((m) => m.id === normalized);\n return model?.reasoning ?? false;\n}\n","/**\n * Usage Logger\n *\n * Logs every LLM request as a JSON line to a daily log file.\n * Files: ~/.openclaw/blockrun/logs/usage-YYYY-MM-DD.jsonl\n *\n * MVP: append-only JSON lines. No rotation, no cleanup.\n * Logging never breaks the request flow — all errors are swallowed.\n */\n\nimport { appendFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport type UsageEntry = {\n timestamp: string;\n model: string;\n tier: string;\n cost: number;\n baselineCost: number;\n savings: number; // 0-1 percentage\n latencyMs: number;\n};\n\nconst LOG_DIR = join(homedir(), \".openclaw\", \"blockrun\", \"logs\");\nlet dirReady = false;\n\nasync function ensureDir(): Promise<void> {\n if (dirReady) return;\n await mkdir(LOG_DIR, { recursive: true });\n dirReady = true;\n}\n\n/**\n * Log a usage entry as a JSON line.\n */\nexport async function logUsage(entry: UsageEntry): Promise<void> {\n try {\n await ensureDir();\n const date = entry.timestamp.slice(0, 10); // YYYY-MM-DD\n const file = join(LOG_DIR, `usage-${date}.jsonl`);\n await appendFile(file, JSON.stringify(entry) + \"\\n\");\n } catch {\n // Never break the request flow\n }\n}\n","/**\n * Usage Statistics Aggregator\n *\n * Reads usage log files and aggregates statistics for terminal display.\n * Supports filtering by date range and provides multiple aggregation views.\n */\n\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { UsageEntry } from \"./logger.js\";\n\nconst LOG_DIR = join(homedir(), \".openclaw\", \"blockrun\", \"logs\");\n\nexport type DailyStats = {\n date: string;\n totalRequests: number;\n totalCost: number;\n totalBaselineCost: number;\n totalSavings: number;\n avgLatencyMs: number;\n byTier: Record<string, { count: number; cost: number }>;\n byModel: Record<string, { count: number; cost: number }>;\n};\n\nexport type AggregatedStats = {\n period: string;\n totalRequests: number;\n totalCost: number;\n totalBaselineCost: number;\n totalSavings: number;\n savingsPercentage: number;\n avgLatencyMs: number;\n avgCostPerRequest: number;\n byTier: Record<string, { count: number; cost: number; percentage: number }>;\n byModel: Record<string, { count: number; cost: number; percentage: number }>;\n dailyBreakdown: DailyStats[];\n entriesWithBaseline: number; // Entries with valid baseline tracking\n};\n\n/**\n * Parse a JSONL log file into usage entries.\n * Handles both old format (without tier/baselineCost) and new format.\n */\nasync function parseLogFile(filePath: string): Promise<UsageEntry[]> {\n try {\n const content = await readFile(filePath, \"utf-8\");\n const lines = content.trim().split(\"\\n\").filter(Boolean);\n return lines.map((line) => {\n const entry = JSON.parse(line) as Partial<UsageEntry>;\n // Handle old format entries\n return {\n timestamp: entry.timestamp || new Date().toISOString(),\n model: entry.model || \"unknown\",\n tier: entry.tier || \"UNKNOWN\",\n cost: entry.cost || 0,\n baselineCost: entry.baselineCost || entry.cost || 0,\n savings: entry.savings || 0,\n latencyMs: entry.latencyMs || 0,\n };\n });\n } catch {\n return [];\n }\n}\n\n/**\n * Get list of available log files sorted by date (newest first).\n */\nasync function getLogFiles(): Promise<string[]> {\n try {\n const files = await readdir(LOG_DIR);\n return files\n .filter((f) => f.startsWith(\"usage-\") && f.endsWith(\".jsonl\"))\n .sort()\n .reverse();\n } catch {\n return [];\n }\n}\n\n/**\n * Aggregate stats for a single day.\n */\nfunction aggregateDay(date: string, entries: UsageEntry[]): DailyStats {\n const byTier: Record<string, { count: number; cost: number }> = {};\n const byModel: Record<string, { count: number; cost: number }> = {};\n let totalLatency = 0;\n\n for (const entry of entries) {\n // By tier\n if (!byTier[entry.tier]) byTier[entry.tier] = { count: 0, cost: 0 };\n byTier[entry.tier].count++;\n byTier[entry.tier].cost += entry.cost;\n\n // By model\n if (!byModel[entry.model]) byModel[entry.model] = { count: 0, cost: 0 };\n byModel[entry.model].count++;\n byModel[entry.model].cost += entry.cost;\n\n totalLatency += entry.latencyMs;\n }\n\n const totalCost = entries.reduce((sum, e) => sum + e.cost, 0);\n const totalBaselineCost = entries.reduce((sum, e) => sum + e.baselineCost, 0);\n\n return {\n date,\n totalRequests: entries.length,\n totalCost,\n totalBaselineCost,\n totalSavings: totalBaselineCost - totalCost,\n avgLatencyMs: entries.length > 0 ? totalLatency / entries.length : 0,\n byTier,\n byModel,\n };\n}\n\n/**\n * Get aggregated statistics for the last N days.\n */\nexport async function getStats(days: number = 7): Promise<AggregatedStats> {\n const logFiles = await getLogFiles();\n const filesToRead = logFiles.slice(0, days);\n\n const dailyBreakdown: DailyStats[] = [];\n const allByTier: Record<string, { count: number; cost: number }> = {};\n const allByModel: Record<string, { count: number; cost: number }> = {};\n let totalRequests = 0;\n let totalCost = 0;\n let totalBaselineCost = 0;\n let totalLatency = 0;\n\n for (const file of filesToRead) {\n const date = file.replace(\"usage-\", \"\").replace(\".jsonl\", \"\");\n const filePath = join(LOG_DIR, file);\n const entries = await parseLogFile(filePath);\n\n if (entries.length === 0) continue;\n\n const dayStats = aggregateDay(date, entries);\n dailyBreakdown.push(dayStats);\n\n totalRequests += dayStats.totalRequests;\n totalCost += dayStats.totalCost;\n totalBaselineCost += dayStats.totalBaselineCost;\n totalLatency += dayStats.avgLatencyMs * dayStats.totalRequests;\n\n // Merge tier stats\n for (const [tier, stats] of Object.entries(dayStats.byTier)) {\n if (!allByTier[tier]) allByTier[tier] = { count: 0, cost: 0 };\n allByTier[tier].count += stats.count;\n allByTier[tier].cost += stats.cost;\n }\n\n // Merge model stats\n for (const [model, stats] of Object.entries(dayStats.byModel)) {\n if (!allByModel[model]) allByModel[model] = { count: 0, cost: 0 };\n allByModel[model].count += stats.count;\n allByModel[model].cost += stats.cost;\n }\n }\n\n // Calculate percentages\n const byTierWithPercentage: Record<string, { count: number; cost: number; percentage: number }> =\n {};\n for (const [tier, stats] of Object.entries(allByTier)) {\n byTierWithPercentage[tier] = {\n ...stats,\n percentage: totalRequests > 0 ? (stats.count / totalRequests) * 100 : 0,\n };\n }\n\n const byModelWithPercentage: Record<string, { count: number; cost: number; percentage: number }> =\n {};\n for (const [model, stats] of Object.entries(allByModel)) {\n byModelWithPercentage[model] = {\n ...stats,\n percentage: totalRequests > 0 ? (stats.count / totalRequests) * 100 : 0,\n };\n }\n\n const totalSavings = totalBaselineCost - totalCost;\n const savingsPercentage = totalBaselineCost > 0 ? (totalSavings / totalBaselineCost) * 100 : 0;\n\n // Count entries with valid baseline tracking (baseline != cost means tracking was active)\n let entriesWithBaseline = 0;\n for (const day of dailyBreakdown) {\n if (day.totalBaselineCost !== day.totalCost) {\n entriesWithBaseline += day.totalRequests;\n }\n }\n\n return {\n period: days === 1 ? \"today\" : `last ${days} days`,\n totalRequests,\n totalCost,\n totalBaselineCost,\n totalSavings,\n savingsPercentage,\n avgLatencyMs: totalRequests > 0 ? totalLatency / totalRequests : 0,\n avgCostPerRequest: totalRequests > 0 ? totalCost / totalRequests : 0,\n byTier: byTierWithPercentage,\n byModel: byModelWithPercentage,\n dailyBreakdown: dailyBreakdown.reverse(), // Oldest first for charts\n entriesWithBaseline, // How many entries have valid baseline tracking\n };\n}\n\n/**\n * Format stats as ASCII table for terminal display.\n */\nexport function formatStatsAscii(stats: AggregatedStats): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"╔════════════════════════════════════════════════════════════╗\");\n lines.push(\"║ ClawRouter Usage Statistics ║\");\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n\n // Summary\n lines.push(`║ Period: ${stats.period.padEnd(49)}║`);\n lines.push(`║ Total Requests: ${stats.totalRequests.toString().padEnd(41)}║`);\n lines.push(`║ Total Cost: $${stats.totalCost.toFixed(4).padEnd(43)}║`);\n lines.push(`║ Baseline Cost (Opus): $${stats.totalBaselineCost.toFixed(4).padEnd(33)}║`);\n\n // Show savings with note if some entries lack baseline tracking\n const savingsLine = `║ 💰 Total Saved: $${stats.totalSavings.toFixed(4)} (${stats.savingsPercentage.toFixed(1)}%)`;\n if (stats.entriesWithBaseline < stats.totalRequests && stats.entriesWithBaseline > 0) {\n lines.push(savingsLine.padEnd(61) + \"║\");\n const note = `║ (based on ${stats.entriesWithBaseline}/${stats.totalRequests} tracked requests)`;\n lines.push(note.padEnd(61) + \"║\");\n } else {\n lines.push(savingsLine.padEnd(61) + \"║\");\n }\n lines.push(`║ Avg Latency: ${stats.avgLatencyMs.toFixed(0)}ms`.padEnd(61) + \"║\");\n\n // Tier breakdown\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n lines.push(\"║ Routing by Tier: ║\");\n\n // Show all tiers found in data, ordered by known tiers first then others\n const knownTiers = [\"SIMPLE\", \"MEDIUM\", \"COMPLEX\", \"REASONING\"];\n const allTiers = Object.keys(stats.byTier);\n const otherTiers = allTiers.filter((t) => !knownTiers.includes(t));\n const tierOrder = [...knownTiers.filter((t) => stats.byTier[t]), ...otherTiers];\n\n for (const tier of tierOrder) {\n const data = stats.byTier[tier];\n if (data) {\n const bar = \"█\".repeat(Math.min(20, Math.round(data.percentage / 5)));\n const displayTier = tier === \"UNKNOWN\" ? \"OTHER\" : tier;\n const line = `║ ${displayTier.padEnd(10)} ${bar.padEnd(20)} ${data.percentage.toFixed(1).padStart(5)}% (${data.count})`;\n lines.push(line.padEnd(61) + \"║\");\n }\n }\n\n // Top models\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n lines.push(\"║ Top Models: ║\");\n\n const sortedModels = Object.entries(stats.byModel)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, 5);\n\n for (const [model, data] of sortedModels) {\n const shortModel = model.length > 25 ? model.slice(0, 22) + \"...\" : model;\n const line = `║ ${shortModel.padEnd(25)} ${data.count.toString().padStart(5)} reqs $${data.cost.toFixed(4)}`;\n lines.push(line.padEnd(61) + \"║\");\n }\n\n // Daily breakdown (last 7 days)\n if (stats.dailyBreakdown.length > 0) {\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n lines.push(\"║ Daily Breakdown: ║\");\n lines.push(\"║ Date Requests Cost Saved ║\");\n\n for (const day of stats.dailyBreakdown.slice(-7)) {\n const saved = day.totalBaselineCost - day.totalCost;\n const line = `║ ${day.date} ${day.totalRequests.toString().padStart(6)} $${day.totalCost.toFixed(4).padStart(8)} $${saved.toFixed(4)}`;\n lines.push(line.padEnd(61) + \"║\");\n }\n }\n\n lines.push(\"╚════════════════════════════════════════════════════════════╝\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * Request Deduplication\n *\n * Prevents double-charging when OpenClaw retries a request after timeout.\n * Tracks in-flight requests and caches completed responses for a short TTL.\n */\n\nimport { createHash } from \"node:crypto\";\n\nexport type CachedResponse = {\n status: number;\n headers: Record<string, string>;\n body: Buffer;\n completedAt: number;\n};\n\ntype InflightEntry = {\n resolve: (result: CachedResponse) => void;\n waiters: Promise<CachedResponse>[];\n};\n\nconst DEFAULT_TTL_MS = 30_000; // 30 seconds\nconst MAX_BODY_SIZE = 1_048_576; // 1MB\n\n/**\n * Canonicalize JSON by sorting object keys recursively.\n * Ensures identical logical content produces identical string regardless of field order.\n */\nfunction canonicalize(obj: unknown): unknown {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(canonicalize);\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = canonicalize((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n}\n\n/**\n * Strip OpenClaw-injected timestamps from message content.\n * Format: [DAY YYYY-MM-DD HH:MM TZ] at the start of messages.\n * Example: [SUN 2026-02-07 13:30 PST] Hello world\n *\n * This ensures requests with different timestamps but same content hash identically.\n */\nconst TIMESTAMP_PATTERN = /^\\[\\w{3}\\s+\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}\\s+\\w+\\]\\s*/;\n\nfunction stripTimestamps(obj: unknown): unknown {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(stripTimestamps);\n }\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (key === \"content\" && typeof value === \"string\") {\n // Strip timestamp prefix from message content\n result[key] = value.replace(TIMESTAMP_PATTERN, \"\");\n } else {\n result[key] = stripTimestamps(value);\n }\n }\n return result;\n}\n\nexport class RequestDeduplicator {\n private inflight = new Map<string, InflightEntry>();\n private completed = new Map<string, CachedResponse>();\n private ttlMs: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS) {\n this.ttlMs = ttlMs;\n }\n\n /** Hash request body to create a dedup key. */\n static hash(body: Buffer): string {\n // Canonicalize JSON to ensure consistent hashing regardless of field order.\n // Also strip OpenClaw-injected timestamps so retries with different timestamps\n // still match the same dedup key.\n let content = body;\n try {\n const parsed = JSON.parse(body.toString());\n const stripped = stripTimestamps(parsed);\n const canonical = canonicalize(stripped);\n content = Buffer.from(JSON.stringify(canonical));\n } catch {\n // Not valid JSON, use raw bytes\n }\n return createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n }\n\n /** Check if a response is cached for this key. */\n getCached(key: string): CachedResponse | undefined {\n const entry = this.completed.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.completedAt > this.ttlMs) {\n this.completed.delete(key);\n return undefined;\n }\n return entry;\n }\n\n /** Check if a request with this key is currently in-flight. Returns a promise to wait on. */\n getInflight(key: string): Promise<CachedResponse> | undefined {\n const entry = this.inflight.get(key);\n if (!entry) return undefined;\n const promise = new Promise<CachedResponse>((resolve) => {\n // Will be resolved when the original request completes\n entry.waiters.push(\n new Promise<CachedResponse>((r) => {\n const orig = entry.resolve;\n entry.resolve = (result) => {\n orig(result);\n resolve(result);\n r(result);\n };\n }),\n );\n });\n return promise;\n }\n\n /** Mark a request as in-flight. */\n markInflight(key: string): void {\n this.inflight.set(key, {\n resolve: () => {},\n waiters: [],\n });\n }\n\n /** Complete an in-flight request — cache result and notify waiters. */\n complete(key: string, result: CachedResponse): void {\n // Only cache responses within size limit\n if (result.body.length <= MAX_BODY_SIZE) {\n this.completed.set(key, result);\n }\n\n const entry = this.inflight.get(key);\n if (entry) {\n entry.resolve(result);\n this.inflight.delete(key);\n }\n\n this.prune();\n }\n\n /** Remove an in-flight entry on error (don't cache failures). */\n removeInflight(key: string): void {\n this.inflight.delete(key);\n }\n\n /** Prune expired completed entries. */\n private prune(): void {\n const now = Date.now();\n for (const [key, entry] of this.completed) {\n if (now - entry.completedAt > this.ttlMs) {\n this.completed.delete(key);\n }\n }\n }\n}\n","/**\n * Balance Monitor for ClawRouter\n *\n * Monitors USDC balance on Base network with intelligent caching.\n * Provides pre-request balance checks to prevent failed payments.\n *\n * Caching Strategy:\n * - TTL: 30 seconds (balance is cached to avoid excessive RPC calls)\n * - Optimistic deduction: after successful payment, subtract estimated cost from cache\n * - Invalidation: on payment failure, immediately refresh from RPC\n */\n\nimport { createPublicClient, http, erc20Abi } from \"viem\";\nimport { base } from \"viem/chains\";\nimport { RpcError } from \"./errors.js\";\n\n/** USDC contract address on Base mainnet */\nconst USDC_BASE = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\" as const;\n\n/** Cache TTL in milliseconds (30 seconds) */\nconst CACHE_TTL_MS = 30_000;\n\n/** Balance thresholds in USDC smallest unit (6 decimals) */\nexport const BALANCE_THRESHOLDS = {\n /** Low balance warning threshold: $1.00 */\n LOW_BALANCE_MICROS: 1_000_000n,\n /** Effectively zero threshold: $0.0001 (covers dust/rounding) */\n ZERO_THRESHOLD: 100n,\n} as const;\n\n/** Balance information returned by checkBalance() */\nexport type BalanceInfo = {\n /** Raw balance in USDC smallest unit (6 decimals) */\n balance: bigint;\n /** Formatted balance as \"$X.XX\" */\n balanceUSD: string;\n /** True if balance < $1.00 */\n isLow: boolean;\n /** True if balance < $0.0001 (effectively zero) */\n isEmpty: boolean;\n /** Wallet address for funding instructions */\n walletAddress: string;\n};\n\n/** Result from checkSufficient() */\nexport type SufficiencyResult = {\n /** True if balance >= estimated cost */\n sufficient: boolean;\n /** Current balance info */\n info: BalanceInfo;\n /** If insufficient, the shortfall as \"$X.XX\" */\n shortfall?: string;\n};\n\n/**\n * Monitors USDC balance on Base network.\n *\n * Usage:\n * const monitor = new BalanceMonitor(\"0x...\");\n * const info = await monitor.checkBalance();\n * if (info.isLow) console.warn(\"Low balance!\");\n */\nexport class BalanceMonitor {\n private readonly client;\n private readonly walletAddress: `0x${string}`;\n\n /** Cached balance (null = not yet fetched) */\n private cachedBalance: bigint | null = null;\n /** Timestamp when cache was last updated */\n private cachedAt = 0;\n\n constructor(walletAddress: string) {\n this.walletAddress = walletAddress as `0x${string}`;\n this.client = createPublicClient({\n chain: base,\n transport: http(undefined, {\n timeout: 10_000, // 10 second timeout to prevent hanging on slow RPC\n }),\n });\n }\n\n /**\n * Check current USDC balance.\n * Uses cache if valid, otherwise fetches from RPC.\n */\n async checkBalance(): Promise<BalanceInfo> {\n const now = Date.now();\n\n // Use cache if valid\n if (this.cachedBalance !== null && now - this.cachedAt < CACHE_TTL_MS) {\n return this.buildInfo(this.cachedBalance);\n }\n\n // Fetch from RPC\n const balance = await this.fetchBalance();\n this.cachedBalance = balance;\n this.cachedAt = now;\n\n return this.buildInfo(balance);\n }\n\n /**\n * Check if balance is sufficient for an estimated cost.\n *\n * @param estimatedCostMicros - Estimated cost in USDC smallest unit (6 decimals)\n */\n async checkSufficient(estimatedCostMicros: bigint): Promise<SufficiencyResult> {\n const info = await this.checkBalance();\n\n if (info.balance >= estimatedCostMicros) {\n return { sufficient: true, info };\n }\n\n const shortfall = estimatedCostMicros - info.balance;\n return {\n sufficient: false,\n info,\n shortfall: this.formatUSDC(shortfall),\n };\n }\n\n /**\n * Optimistically deduct estimated cost from cached balance.\n * Call this after a successful payment to keep cache accurate.\n *\n * @param amountMicros - Amount to deduct in USDC smallest unit\n */\n deductEstimated(amountMicros: bigint): void {\n if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {\n this.cachedBalance -= amountMicros;\n }\n }\n\n /**\n * Invalidate cache, forcing next checkBalance() to fetch from RPC.\n * Call this after a payment failure to get accurate balance.\n */\n invalidate(): void {\n this.cachedBalance = null;\n this.cachedAt = 0;\n }\n\n /**\n * Force refresh balance from RPC (ignores cache).\n */\n async refresh(): Promise<BalanceInfo> {\n this.invalidate();\n return this.checkBalance();\n }\n\n /**\n * Format USDC amount (in micros) as \"$X.XX\".\n */\n formatUSDC(amountMicros: bigint): string {\n // USDC has 6 decimals\n const dollars = Number(amountMicros) / 1_000_000;\n return `$${dollars.toFixed(2)}`;\n }\n\n /**\n * Get the wallet address being monitored.\n */\n getWalletAddress(): string {\n return this.walletAddress;\n }\n\n /** Fetch balance from RPC */\n private async fetchBalance(): Promise<bigint> {\n try {\n const balance = await this.client.readContract({\n address: USDC_BASE,\n abi: erc20Abi,\n functionName: \"balanceOf\",\n args: [this.walletAddress],\n });\n return balance;\n } catch (error) {\n // Throw typed error instead of silently returning 0\n // This allows callers to distinguish \"node down\" from \"wallet empty\"\n throw new RpcError(error instanceof Error ? error.message : \"Unknown error\", error);\n }\n }\n\n /** Build BalanceInfo from raw balance */\n private buildInfo(balance: bigint): BalanceInfo {\n return {\n balance,\n balanceUSD: this.formatUSDC(balance),\n isLow: balance < BALANCE_THRESHOLDS.LOW_BALANCE_MICROS,\n isEmpty: balance < BALANCE_THRESHOLDS.ZERO_THRESHOLD,\n walletAddress: this.walletAddress,\n };\n }\n}\n","/**\n * Typed Error Classes for ClawRouter\n *\n * Provides structured errors for balance-related failures with\n * all necessary information for user-friendly error messages.\n */\n\n/**\n * Thrown when wallet has insufficient USDC balance for a request.\n */\nexport class InsufficientFundsError extends Error {\n readonly code = \"INSUFFICIENT_FUNDS\" as const;\n readonly currentBalanceUSD: string;\n readonly requiredUSD: string;\n readonly walletAddress: string;\n\n constructor(opts: { currentBalanceUSD: string; requiredUSD: string; walletAddress: string }) {\n const msg = [\n `Insufficient balance. Current: ${opts.currentBalanceUSD}, Required: ${opts.requiredUSD}`,\n `Options:`,\n ` 1. Fund wallet: ${opts.walletAddress}`,\n ` 2. Use free model: /model free`,\n ].join(\"\\n\");\n super(msg);\n this.name = \"InsufficientFundsError\";\n this.currentBalanceUSD = opts.currentBalanceUSD;\n this.requiredUSD = opts.requiredUSD;\n this.walletAddress = opts.walletAddress;\n }\n}\n\n/**\n * Thrown when wallet has no USDC balance (or effectively zero).\n */\nexport class EmptyWalletError extends Error {\n readonly code = \"EMPTY_WALLET\" as const;\n readonly walletAddress: string;\n\n constructor(walletAddress: string) {\n const msg = [\n `No USDC balance.`,\n `Options:`,\n ` 1. Fund wallet: ${walletAddress}`,\n ` 2. Use free model: /model free`,\n ` 3. Uninstall: bash ~/.openclaw/extensions/clawrouter/scripts/uninstall.sh`,\n ].join(\"\\n\");\n super(msg);\n this.name = \"EmptyWalletError\";\n this.walletAddress = walletAddress;\n }\n}\n\n/**\n * Type guard to check if an error is InsufficientFundsError.\n */\nexport function isInsufficientFundsError(error: unknown): error is InsufficientFundsError {\n return error instanceof Error && (error as InsufficientFundsError).code === \"INSUFFICIENT_FUNDS\";\n}\n\n/**\n * Type guard to check if an error is EmptyWalletError.\n */\nexport function isEmptyWalletError(error: unknown): error is EmptyWalletError {\n return error instanceof Error && (error as EmptyWalletError).code === \"EMPTY_WALLET\";\n}\n\n/**\n * Type guard to check if an error is a balance-related error.\n */\nexport function isBalanceError(error: unknown): error is InsufficientFundsError | EmptyWalletError {\n return isInsufficientFundsError(error) || isEmptyWalletError(error);\n}\n\n/**\n * Thrown when RPC call fails (network error, node down, etc).\n * Distinguishes infrastructure failures from actual empty wallets.\n */\nexport class RpcError extends Error {\n readonly code = \"RPC_ERROR\" as const;\n readonly originalError: unknown;\n\n constructor(message: string, originalError?: unknown) {\n super(`RPC error: ${message}. Check network connectivity.`);\n this.name = \"RpcError\";\n this.originalError = originalError;\n }\n}\n\n/**\n * Type guard to check if an error is RpcError.\n */\nexport function isRpcError(error: unknown): error is RpcError {\n return error instanceof Error && (error as RpcError).code === \"RPC_ERROR\";\n}\n","/**\n * Single source of truth for version.\n * Reads from package.json at build time via tsup's define.\n */\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\n\n// Read package.json at runtime\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// In dist/, go up one level to find package.json\nconst require = createRequire(import.meta.url);\nconst pkg = require(join(__dirname, \"..\", \"package.json\")) as { version: string };\n\nexport const VERSION = pkg.version;\nexport const USER_AGENT = `clawrouter/${VERSION}`;\n","/**\n * Session Persistence Store\n *\n * Tracks model selections per session to prevent model switching mid-task.\n * When a session is active, the router will continue using the same model\n * instead of re-routing each request.\n */\n\nexport type SessionEntry = {\n model: string;\n tier: string;\n createdAt: number;\n lastUsedAt: number;\n requestCount: number;\n};\n\nexport type SessionConfig = {\n /** Enable session persistence (default: false) */\n enabled: boolean;\n /** Session timeout in ms (default: 30 minutes) */\n timeoutMs: number;\n /** Header name for session ID (default: X-Session-ID) */\n headerName: string;\n};\n\nexport const DEFAULT_SESSION_CONFIG: SessionConfig = {\n enabled: false,\n timeoutMs: 30 * 60 * 1000, // 30 minutes\n headerName: \"x-session-id\",\n};\n\n/**\n * Session persistence store for maintaining model selections.\n */\nexport class SessionStore {\n private sessions: Map<string, SessionEntry> = new Map();\n private config: SessionConfig;\n private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor(config: Partial<SessionConfig> = {}) {\n this.config = { ...DEFAULT_SESSION_CONFIG, ...config };\n\n // Start cleanup interval (every 5 minutes)\n if (this.config.enabled) {\n this.cleanupInterval = setInterval(() => this.cleanup(), 5 * 60 * 1000);\n }\n }\n\n /**\n * Get the pinned model for a session, if any.\n */\n getSession(sessionId: string): SessionEntry | undefined {\n if (!this.config.enabled || !sessionId) {\n return undefined;\n }\n\n const entry = this.sessions.get(sessionId);\n if (!entry) {\n return undefined;\n }\n\n // Check if session has expired\n const now = Date.now();\n if (now - entry.lastUsedAt > this.config.timeoutMs) {\n this.sessions.delete(sessionId);\n return undefined;\n }\n\n return entry;\n }\n\n /**\n * Pin a model to a session.\n */\n setSession(sessionId: string, model: string, tier: string): void {\n if (!this.config.enabled || !sessionId) {\n return;\n }\n\n const existing = this.sessions.get(sessionId);\n const now = Date.now();\n\n if (existing) {\n existing.lastUsedAt = now;\n existing.requestCount++;\n // Update model if different (e.g., fallback)\n if (existing.model !== model) {\n existing.model = model;\n existing.tier = tier;\n }\n } else {\n this.sessions.set(sessionId, {\n model,\n tier,\n createdAt: now,\n lastUsedAt: now,\n requestCount: 1,\n });\n }\n }\n\n /**\n * Touch a session to extend its timeout.\n */\n touchSession(sessionId: string): void {\n if (!this.config.enabled || !sessionId) {\n return;\n }\n\n const entry = this.sessions.get(sessionId);\n if (entry) {\n entry.lastUsedAt = Date.now();\n entry.requestCount++;\n }\n }\n\n /**\n * Clear a specific session.\n */\n clearSession(sessionId: string): void {\n this.sessions.delete(sessionId);\n }\n\n /**\n * Clear all sessions.\n */\n clearAll(): void {\n this.sessions.clear();\n }\n\n /**\n * Get session stats for debugging.\n */\n getStats(): { count: number; sessions: Array<{ id: string; model: string; age: number }> } {\n const now = Date.now();\n const sessions = Array.from(this.sessions.entries()).map(([id, entry]) => ({\n id: id.slice(0, 8) + \"...\",\n model: entry.model,\n age: Math.round((now - entry.createdAt) / 1000),\n }));\n return { count: this.sessions.size, sessions };\n }\n\n /**\n * Clean up expired sessions.\n */\n private cleanup(): void {\n const now = Date.now();\n for (const [id, entry] of this.sessions) {\n if (now - entry.lastUsedAt > this.config.timeoutMs) {\n this.sessions.delete(id);\n }\n }\n }\n\n /**\n * Stop the cleanup interval.\n */\n close(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n}\n\n/**\n * Generate a session ID from request headers or create a default.\n */\nexport function getSessionId(\n headers: Record<string, string | string[] | undefined>,\n headerName: string = DEFAULT_SESSION_CONFIG.headerName,\n): string | undefined {\n const value = headers[headerName] || headers[headerName.toLowerCase()];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && value.length > 0) {\n return value[0];\n }\n return undefined;\n}\n","/**\n * BlockRun Auth Methods for OpenClaw\n *\n * Provides wallet-based authentication for the BlockRun provider.\n * Operators configure their wallet private key, which is used to\n * sign x402 micropayments for LLM inference.\n *\n * Three methods:\n * 1. Auto-generate — create a new wallet on first run, save to ~/.openclaw/blockrun/wallet.key\n * 2. Environment variable — read from BLOCKRUN_WALLET_KEY\n * 3. Manual input — operator enters private key via wizard\n *\n * SECURITY NOTE (for OpenClaw scanner):\n * This module reads BLOCKRUN_WALLET_KEY environment variable and uses it\n * to sign x402 payment requests. This is INTENTIONAL and REQUIRED behavior:\n * - The wallet key signs USDC payments on Base L2 for each LLM API call\n * - Without the key, ClawRouter cannot authorize payments to BlockRun\n * - The key is NEVER transmitted over the network, only used locally for signing\n * - This is standard x402 payment flow, not credential harvesting\n *\n * @see https://x402.org - x402 payment protocol specification\n * @see https://blockrun.ai/docs - BlockRun API documentation\n * @openclaw-security env-access=BLOCKRUN_WALLET_KEY purpose=x402-payment-signing\n */\n\nimport { writeFile, readFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { generatePrivateKey, privateKeyToAccount } from \"viem/accounts\";\nimport type { ProviderAuthMethod, ProviderAuthContext, ProviderAuthResult } from \"./types.js\";\n\nconst WALLET_DIR = join(homedir(), \".openclaw\", \"blockrun\");\nconst WALLET_FILE = join(WALLET_DIR, \"wallet.key\");\n\n// Export for use by wallet command\nexport { WALLET_FILE };\n\n/**\n * Try to load a previously auto-generated wallet key from disk.\n */\nasync function loadSavedWallet(): Promise<string | undefined> {\n try {\n const key = (await readFile(WALLET_FILE, \"utf-8\")).trim();\n if (key.startsWith(\"0x\") && key.length === 66) return key;\n } catch {\n // File doesn't exist yet\n }\n return undefined;\n}\n\n/**\n * Generate a new wallet, save to disk, return the private key.\n */\nasync function generateAndSaveWallet(): Promise<{ key: string; address: string }> {\n const key = generatePrivateKey();\n const account = privateKeyToAccount(key);\n await mkdir(WALLET_DIR, { recursive: true });\n await writeFile(WALLET_FILE, key + \"\\n\", { mode: 0o600 });\n return { key, address: account.address };\n}\n\n/**\n * Resolve wallet key: load saved → env var → auto-generate.\n * Called by index.ts before the auth wizard runs.\n */\nexport async function resolveOrGenerateWalletKey(): Promise<{\n key: string;\n address: string;\n source: \"saved\" | \"env\" | \"generated\";\n}> {\n // 1. Previously saved wallet\n const saved = await loadSavedWallet();\n if (saved) {\n const account = privateKeyToAccount(saved as `0x${string}`);\n return { key: saved, address: account.address, source: \"saved\" };\n }\n\n // 2. Environment variable\n const envKey = process.env.BLOCKRUN_WALLET_KEY;\n if (typeof envKey === \"string\" && envKey.startsWith(\"0x\") && envKey.length === 66) {\n const account = privateKeyToAccount(envKey as `0x${string}`);\n return { key: envKey, address: account.address, source: \"env\" };\n }\n\n // 3. Auto-generate\n const { key, address } = await generateAndSaveWallet();\n return { key, address, source: \"generated\" };\n}\n\n/**\n * Auth method: operator enters their wallet private key directly.\n */\nexport const walletKeyAuth: ProviderAuthMethod = {\n id: \"wallet-key\",\n label: \"Wallet Private Key\",\n hint: \"Enter your EVM wallet private key (0x...) for x402 payments to BlockRun\",\n kind: \"api_key\",\n run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {\n const key = await ctx.prompter.text({\n message: \"Enter your wallet private key (0x...)\",\n validate: (value: string) => {\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"0x\")) return \"Key must start with 0x\";\n if (trimmed.length !== 66) return \"Key must be 66 characters (0x + 64 hex)\";\n if (!/^0x[0-9a-fA-F]{64}$/.test(trimmed)) return \"Key must be valid hex\";\n return undefined;\n },\n });\n\n if (!key || typeof key !== \"string\") {\n throw new Error(\"Wallet key is required\");\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\n \"Wallet key stored securely in OpenClaw credentials.\",\n \"Your wallet signs x402 USDC payments on Base for each LLM call.\",\n \"Fund your wallet with USDC on Base to start using BlockRun models.\",\n ],\n };\n },\n};\n\n/**\n * Auth method: read wallet key from BLOCKRUN_WALLET_KEY environment variable.\n */\nexport const envKeyAuth: ProviderAuthMethod = {\n id: \"env-key\",\n label: \"Environment Variable\",\n hint: \"Use BLOCKRUN_WALLET_KEY environment variable\",\n kind: \"api_key\",\n run: async (): Promise<ProviderAuthResult> => {\n const key = process.env.BLOCKRUN_WALLET_KEY;\n\n if (!key) {\n throw new Error(\n \"BLOCKRUN_WALLET_KEY environment variable is not set. \" +\n \"Set it to your EVM wallet private key (0x...).\",\n );\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\"Using wallet key from BLOCKRUN_WALLET_KEY environment variable.\"],\n };\n },\n};\n","#!/usr/bin/env node\n/**\n * ClawRouter CLI\n *\n * Standalone proxy for deployed setups where the proxy needs to survive gateway restarts.\n *\n * Usage:\n * npx @blockrun/clawrouter # Start standalone proxy\n * npx @blockrun/clawrouter --version # Show version\n * npx @blockrun/clawrouter --port 8402 # Custom port\n *\n * For production deployments, use with PM2:\n * pm2 start \"npx @blockrun/clawrouter\" --name clawrouter\n */\n\nimport { startProxy, getProxyPort } from \"./proxy.js\";\nimport { resolveOrGenerateWalletKey } from \"./auth.js\";\nimport { BalanceMonitor } from \"./balance.js\";\nimport { VERSION } from \"./version.js\";\n\nfunction printHelp(): void {\n console.log(`\nClawRouter v${VERSION} - Smart LLM Router\n\nUsage:\n clawrouter [options]\n\nOptions:\n --version, -v Show version number\n --help, -h Show this help message\n --port <number> Port to listen on (default: ${getProxyPort()})\n\nExamples:\n # Start standalone proxy (survives gateway restarts)\n npx @blockrun/clawrouter\n\n # Start on custom port\n npx @blockrun/clawrouter --port 9000\n\n # Production deployment with PM2\n pm2 start \"npx @blockrun/clawrouter\" --name clawrouter\n\nEnvironment Variables:\n BLOCKRUN_WALLET_KEY Private key for x402 payments (auto-generated if not set)\n BLOCKRUN_PROXY_PORT Default proxy port (default: 8402)\n\nFor more info: https://github.com/BlockRunAI/ClawRouter\n`);\n}\n\nfunction parseArgs(args: string[]): { version: boolean; help: boolean; port?: number } {\n const result = { version: false, help: false, port: undefined as number | undefined };\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--version\" || arg === \"-v\") {\n result.version = true;\n } else if (arg === \"--help\" || arg === \"-h\") {\n result.help = true;\n } else if (arg === \"--port\" && args[i + 1]) {\n result.port = parseInt(args[i + 1], 10);\n i++; // Skip next arg\n }\n }\n\n return result;\n}\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv.slice(2));\n\n if (args.version) {\n console.log(VERSION);\n process.exit(0);\n }\n\n if (args.help) {\n printHelp();\n process.exit(0);\n }\n\n // Resolve wallet key\n const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();\n\n if (source === \"generated\") {\n console.log(`[ClawRouter] Generated new wallet: ${address}`);\n } else if (source === \"saved\") {\n console.log(`[ClawRouter] Using saved wallet: ${address}`);\n } else {\n console.log(`[ClawRouter] Using wallet from BLOCKRUN_WALLET_KEY: ${address}`);\n }\n\n // Start the proxy\n const proxy = await startProxy({\n walletKey,\n port: args.port,\n onReady: (port) => {\n console.log(`[ClawRouter] Proxy listening on http://127.0.0.1:${port}`);\n console.log(`[ClawRouter] Health check: http://127.0.0.1:${port}/health`);\n },\n onError: (error) => {\n console.error(`[ClawRouter] Error: ${error.message}`);\n },\n onRouted: (decision) => {\n const cost = decision.costEstimate.toFixed(4);\n const saved = (decision.savings * 100).toFixed(0);\n console.log(`[ClawRouter] [${decision.tier}] ${decision.model} $${cost} (saved ${saved}%)`);\n },\n onLowBalance: (info) => {\n console.warn(`[ClawRouter] Low balance: ${info.balanceUSD}. Fund: ${info.walletAddress}`);\n },\n onInsufficientFunds: (info) => {\n console.error(\n `[ClawRouter] Insufficient funds. Balance: ${info.balanceUSD}, Need: ${info.requiredUSD}`,\n );\n },\n });\n\n // Check balance\n const monitor = new BalanceMonitor(address);\n try {\n const balance = await monitor.checkBalance();\n if (balance.isEmpty) {\n console.log(`[ClawRouter] Wallet balance: $0.00 (using FREE model)`);\n console.log(`[ClawRouter] Fund wallet for premium models: ${address}`);\n } else if (balance.isLow) {\n console.log(`[ClawRouter] Wallet balance: ${balance.balanceUSD} (low)`);\n } else {\n console.log(`[ClawRouter] Wallet balance: ${balance.balanceUSD}`);\n }\n } catch {\n console.log(`[ClawRouter] Wallet: ${address} (balance check pending)`);\n }\n\n console.log(`[ClawRouter] Ready - Ctrl+C to stop`);\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\n[ClawRouter] Received ${signal}, shutting down...`);\n try {\n await proxy.close();\n console.log(`[ClawRouter] Proxy closed`);\n process.exit(0);\n } catch (err) {\n console.error(`[ClawRouter] Error during shutdown: ${err}`);\n process.exit(1);\n }\n };\n\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n\n // Keep process alive\n await new Promise(() => {});\n}\n\nmain().catch((err) => {\n console.error(`[ClawRouter] Fatal error: ${err.message}`);\n process.exit(1);\n});\n"],"mappings":"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MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA;AAAA,MAEd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA,IAGA,iBAAiB;AAAA;AAAA,MAEf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,wBAAwB;AAAA;AAAA,MAEtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA;AAAA,IAIA,qBAAqB;AAAA;AAAA,MAEnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA,IAGA,kBAAkB;AAAA,MAChB,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,kBAAkB;AAAA;AAAA,MAClB,mBAAmB;AAAA,MACnB,oBAAoB;AAAA,MACpB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,cAAc;AAAA,MACd,qBAAqB;AAAA,MACrB,oBAAoB;AAAA,MACpB,mBAAmB;AAAA,MACnB,aAAa;AAAA;AAAA,IACf;AAAA;AAAA,IAGA,gBAAgB;AAAA,MACd,cAAc;AAAA,MACd,eAAe;AAAA,MACf,kBAAkB;AAAA;AAAA,IACpB;AAAA;AAAA,IAGA,qBAAqB;AAAA;AAAA,IAErB,qBAAqB;AAAA,EACvB;AAAA,EAEA,OAAO;AAAA,IACL,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,UAAU,CAAC,uBAAuB,0BAA0B,oBAAoB;AAAA,IAClF;AAAA,IACA,QAAQ;AAAA,MACN,SAAS;AAAA;AAAA,MACT,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,SAAS;AAAA,MACT,UAAU,CAAC,6BAA6B,mBAAmB,eAAe;AAAA,IAC5E;AAAA,IACA,WAAW;AAAA,MACT,SAAS;AAAA;AAAA,MACT,UAAU,CAAC,8BAA8B,sBAAsB,uBAAuB;AAAA,IACxF;AAAA,EACF;AAAA;AAAA,EAGA,cAAc;AAAA,IACZ,QAAQ;AAAA,MACN,SAAS;AAAA;AAAA,MACT,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,SAAS;AAAA;AAAA,MACT,UAAU,CAAC,sBAAsB,8BAA8B,2BAA2B;AAAA,IAC5F;AAAA,IACA,SAAS;AAAA,MACP,SAAS;AAAA,MACT,UAAU,CAAC,2BAA2B,mBAAmB,eAAe;AAAA,IAC1E;AAAA,IACA,WAAW;AAAA,MACT,SAAS;AAAA;AAAA,MACT,UAAU,CAAC,6BAA6B,sBAAsB,4BAA4B;AAAA,IAC5F;AAAA,EACF;AAAA,EAEA,WAAW;AAAA,IACT,uBAAuB;AAAA,IACvB,yBAAyB;AAAA,IACzB,sBAAsB;AAAA,IACtB,aAAa;AAAA,EACf;AACF;;;ACtpBO,SAAS,MACd,QACA,cACA,iBACA,SACiB;AACjB,QAAM,EAAE,QAAQ,aAAa,IAAI;AAGjC,QAAM,WAAW,GAAG,gBAAgB,EAAE,IAAI,MAAM;AAChD,QAAM,kBAAkB,KAAK,KAAK,SAAS,SAAS,CAAC;AAGrD,QAAM,aAAa,gBAAgB,QAAQ,cAAc,iBAAiB,OAAO,OAAO;AAKxF,QAAM,eAAe,WAAW,gBAAgB;AAChD,QAAM,gBAAgB,gBAAgB;AACtC,QAAM,oBAAoB,OAAO,UAAU,eAAe;AAC1D,QAAM,mBAAmB,iBAAiB,sBAAsB,OAAO,gBAAgB;AACvF,QAAM,cAAc,kBAAkB,OAAO,eAAgB,OAAO;AAGpE,MAAI,kBAAkB,OAAO,UAAU,uBAAuB;AAC5D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB,OAAO,UAAU,qBAAqB,UAAU,kBAAkB,eAAe,EAAE;AAAA,MACpG;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,sBAAsB,eAAe,0BAA0B,KAAK,YAAY,IAAI;AAE1F,MAAI;AACJ,MAAI;AACJ,QAAM,SAA0B;AAChC,MAAI,YAAY,SAAS,WAAW,MAAM,QAAQ,CAAC,CAAC,MAAM,WAAW,QAAQ,KAAK,IAAI,CAAC;AAEvF,MAAI,WAAW,SAAS,MAAM;AAC5B,WAAO,WAAW;AAClB,iBAAa,WAAW;AAAA,EAC1B,OAAO;AAEL,WAAO,OAAO,UAAU;AACxB,iBAAa;AACb,iBAAa,4BAA4B,IAAI;AAAA,EAC/C;AAGA,MAAI,qBAAqB;AACvB,UAAM,WAAiC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,EAAE;AACxF,UAAM,UAAU,OAAO,UAAU;AACjC,QAAI,SAAS,IAAI,IAAI,SAAS,OAAO,GAAG;AACtC,mBAAa,kBAAkB,OAAO;AACtC,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,eAAe;AACjB,iBAAa;AAAA,EACf,WAAW,mBAAmB;AAC5B,iBAAa;AAAA,EACf;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC7FO,IAAM,gBAAwC;AAAA;AAAA,EAEnD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA;AAAA,EAGP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,IAAI;AAAA;AAAA,EAGJ,UAAU;AAAA,EACV,UAAU;AAAA;AAAA,EAGV,MAAM;AAAA;AAAA,EAGN,QAAQ;AAAA,EACR,OAAO;AAAA;AAAA,EAGP,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aAAa;AAAA;AAAA,EAGb,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,MAAM;AACR;AAMO,SAAS,kBAAkB,OAAuB;AACvD,QAAM,aAAa,MAAM,KAAK,EAAE,YAAY;AAC5C,QAAM,WAAW,cAAc,UAAU;AACzC,MAAI,SAAU,QAAO;AAGrB,MAAI,WAAW,WAAW,WAAW,GAAG;AACtC,UAAM,gBAAgB,WAAW,MAAM,YAAY,MAAM;AACzD,UAAM,wBAAwB,cAAc,aAAa;AACzD,QAAI,sBAAuB,QAAO;AAAA,EACpC;AAEA,SAAO;AACT;AAeO,IAAM,kBAAmC;AAAA;AAAA;AAAA,EAG9C;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,SAAS;AAAA,EACX;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,SAAS;AAAA;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AACF;AAKA,SAAS,gBAAgB,GAAyC;AAChE,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,KAAK;AAAA,IACL,WAAW,EAAE,aAAa;AAAA,IAC1B,OAAO,EAAE,SAAS,CAAC,QAAQ,OAAO,IAAI,CAAC,MAAM;AAAA,IAC7C,MAAM;AAAA,MACJ,OAAO,EAAE;AAAA,MACT,QAAQ,EAAE;AAAA,MACV,WAAW;AAAA,MACX,YAAY;AAAA,IACd;AAAA,IACA,eAAe,EAAE;AAAA,IACjB,WAAW,EAAE;AAAA,EACf;AACF;AAMA,IAAM,eAAwC,OAAO,QAAQ,aAAa,EACvE,IAAI,CAAC,CAAC,OAAO,QAAQ,MAAM;AAC1B,QAAM,SAAS,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAC5D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,gBAAgB,EAAE,GAAG,QAAQ,IAAI,OAAO,MAAM,GAAG,KAAK,WAAM,OAAO,IAAI,GAAG,CAAC;AACpF,CAAC,EACA,OAAO,CAAC,MAAkC,MAAM,IAAI;AAKhD,IAAM,kBAA2C;AAAA,EACtD,GAAG,gBAAgB,IAAI,eAAe;AAAA,EACtC,GAAG;AACL;AAsCO,SAAS,sBAAsB,SAAqC;AACzE,QAAM,aAAa,QAAQ,QAAQ,aAAa,EAAE;AAClD,QAAM,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AAC7D,SAAO,OAAO;AAChB;AAMO,SAAS,iBAAiB,SAA0B;AACzD,QAAM,aAAa,QAAQ,QAAQ,aAAa,EAAE;AAClD,QAAM,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AAC7D,SAAO,OAAO,aAAa;AAC7B;;;ACjhBA,SAAS,YAAY,aAAa;AAClC,SAAS,YAAY;AACrB,SAAS,eAAe;AAYxB,IAAM,UAAU,KAAK,QAAQ,GAAG,aAAa,YAAY,MAAM;AAC/D,IAAI,WAAW;AAEf,eAAe,YAA2B;AACxC,MAAI,SAAU;AACd,QAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACxC,aAAW;AACb;AAKA,eAAsB,SAAS,OAAkC;AAC/D,MAAI;AACF,UAAM,UAAU;AAChB,UAAM,OAAO,MAAM,UAAU,MAAM,GAAG,EAAE;AACxC,UAAM,OAAO,KAAK,SAAS,SAAS,IAAI,QAAQ;AAChD,UAAM,WAAW,MAAM,KAAK,UAAU,KAAK,IAAI,IAAI;AAAA,EACrD,QAAQ;AAAA,EAER;AACF;;;ACtCA,SAAS,UAAU,eAAe;AAClC,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAGxB,IAAMC,WAAUF,MAAKC,SAAQ,GAAG,aAAa,YAAY,MAAM;AAgC/D,eAAe,aAAa,UAAyC;AACnE,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,UAAM,QAAQ,QAAQ,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AACvD,WAAO,MAAM,IAAI,CAAC,SAAS;AACzB,YAAM,QAAQ,KAAK,MAAM,IAAI;AAE7B,aAAO;AAAA,QACL,WAAW,MAAM,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,QACrD,OAAO,MAAM,SAAS;AAAA,QACtB,MAAM,MAAM,QAAQ;AAAA,QACpB,MAAM,MAAM,QAAQ;AAAA,QACpB,cAAc,MAAM,gBAAgB,MAAM,QAAQ;AAAA,QAClD,SAAS,MAAM,WAAW;AAAA,QAC1B,WAAW,MAAM,aAAa;AAAA,MAChC;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAKA,eAAe,cAAiC;AAC9C,MAAI;AACF,UAAM,QAAQ,MAAM,QAAQC,QAAO;AACnC,WAAO,MACJ,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,KAAK,EAAE,SAAS,QAAQ,CAAC,EAC5D,KAAK,EACL,QAAQ;AAAA,EACb,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAKA,SAAS,aAAa,MAAc,SAAmC;AACrE,QAAM,SAA0D,CAAC;AACjE,QAAM,UAA2D,CAAC;AAClE,MAAI,eAAe;AAEnB,aAAW,SAAS,SAAS;AAE3B,QAAI,CAAC,OAAO,MAAM,IAAI,EAAG,QAAO,MAAM,IAAI,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE;AAClE,WAAO,MAAM,IAAI,EAAE;AACnB,WAAO,MAAM,IAAI,EAAE,QAAQ,MAAM;AAGjC,QAAI,CAAC,QAAQ,MAAM,KAAK,EAAG,SAAQ,MAAM,KAAK,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE;AACtE,YAAQ,MAAM,KAAK,EAAE;AACrB,YAAQ,MAAM,KAAK,EAAE,QAAQ,MAAM;AAEnC,oBAAgB,MAAM;AAAA,EACxB;AAEA,QAAM,YAAY,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,CAAC;AAC5D,QAAM,oBAAoB,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,cAAc,CAAC;AAE5E,SAAO;AAAA,IACL;AAAA,IACA,eAAe,QAAQ;AAAA,IACvB;AAAA,IACA;AAAA,IACA,cAAc,oBAAoB;AAAA,IAClC,cAAc,QAAQ,SAAS,IAAI,eAAe,QAAQ,SAAS;AAAA,IACnE;AAAA,IACA;AAAA,EACF;AACF;AAKA,eAAsB,SAAS,OAAe,GAA6B;AACzE,QAAM,WAAW,MAAM,YAAY;AACnC,QAAM,cAAc,SAAS,MAAM,GAAG,IAAI;AAE1C,QAAM,iBAA+B,CAAC;AACtC,QAAM,YAA6D,CAAC;AACpE,QAAM,aAA8D,CAAC;AACrE,MAAI,gBAAgB;AACpB,MAAI,YAAY;AAChB,MAAI,oBAAoB;AACxB,MAAI,eAAe;AAEnB,aAAW,QAAQ,aAAa;AAC9B,UAAM,OAAO,KAAK,QAAQ,UAAU,EAAE,EAAE,QAAQ,UAAU,EAAE;AAC5D,UAAM,WAAWF,MAAKE,UAAS,IAAI;AACnC,UAAM,UAAU,MAAM,aAAa,QAAQ;AAE3C,QAAI,QAAQ,WAAW,EAAG;AAE1B,UAAM,WAAW,aAAa,MAAM,OAAO;AAC3C,mBAAe,KAAK,QAAQ;AAE5B,qBAAiB,SAAS;AAC1B,iBAAa,SAAS;AACtB,yBAAqB,SAAS;AAC9B,oBAAgB,SAAS,eAAe,SAAS;AAGjD,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,SAAS,MAAM,GAAG;AAC3D,UAAI,CAAC,UAAU,IAAI,EAAG,WAAU,IAAI,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE;AAC5D,gBAAU,IAAI,EAAE,SAAS,MAAM;AAC/B,gBAAU,IAAI,EAAE,QAAQ,MAAM;AAAA,IAChC;AAGA,eAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,SAAS,OAAO,GAAG;AAC7D,UAAI,CAAC,WAAW,KAAK,EAAG,YAAW,KAAK,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE;AAChE,iBAAW,KAAK,EAAE,SAAS,MAAM;AACjC,iBAAW,KAAK,EAAE,QAAQ,MAAM;AAAA,IAClC;AAAA,EACF;AAGA,QAAM,uBACJ,CAAC;AACH,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AACrD,yBAAqB,IAAI,IAAI;AAAA,MAC3B,GAAG;AAAA,MACH,YAAY,gBAAgB,IAAK,MAAM,QAAQ,gBAAiB,MAAM;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,wBACJ,CAAC;AACH,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,UAAU,GAAG;AACvD,0BAAsB,KAAK,IAAI;AAAA,MAC7B,GAAG;AAAA,MACH,YAAY,gBAAgB,IAAK,MAAM,QAAQ,gBAAiB,MAAM;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,eAAe,oBAAoB;AACzC,QAAM,oBAAoB,oBAAoB,IAAK,eAAe,oBAAqB,MAAM;AAG7F,MAAI,sBAAsB;AAC1B,aAAW,OAAO,gBAAgB;AAChC,QAAI,IAAI,sBAAsB,IAAI,WAAW;AAC3C,6BAAuB,IAAI;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,SAAS,IAAI,UAAU,QAAQ,IAAI;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,gBAAgB,IAAI,eAAe,gBAAgB;AAAA,IACjE,mBAAmB,gBAAgB,IAAI,YAAY,gBAAgB;AAAA,IACnE,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,gBAAgB,eAAe,QAAQ;AAAA;AAAA,IACvC;AAAA;AAAA,EACF;AACF;;;ACxMA,SAAS,kBAAkB;AAc3B,IAAMC,kBAAiB;AACvB,IAAM,gBAAgB;AAMtB,SAAS,aAAa,KAAuB;AAC3C,MAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,YAAY;AAAA,EAC7B;AACA,QAAM,SAAkC,CAAC;AACzC,aAAW,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK,GAAG;AACzC,WAAO,GAAG,IAAI,aAAc,IAAgC,GAAG,CAAC;AAAA,EAClE;AACA,SAAO;AACT;AASA,IAAM,oBAAoB;AAE1B,SAAS,gBAAgB,KAAuB;AAC9C,MAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,eAAe;AAAA,EAChC;AACA,QAAM,SAAkC,CAAC;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAA8B,GAAG;AACzE,QAAI,QAAQ,aAAa,OAAO,UAAU,UAAU;AAElD,aAAO,GAAG,IAAI,MAAM,QAAQ,mBAAmB,EAAE;AAAA,IACnD,OAAO;AACL,aAAO,GAAG,IAAI,gBAAgB,KAAK;AAAA,IACrC;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,sBAAN,MAA0B;AAAA,EACvB,WAAW,oBAAI,IAA2B;AAAA,EAC1C,YAAY,oBAAI,IAA4B;AAAA,EAC5C;AAAA,EAER,YAAY,QAAQA,iBAAgB;AAClC,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAGA,OAAO,KAAK,MAAsB;AAIhC,QAAI,UAAU;AACd,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK,SAAS,CAAC;AACzC,YAAM,WAAW,gBAAgB,MAAM;AACvC,YAAM,YAAY,aAAa,QAAQ;AACvC,gBAAU,OAAO,KAAK,KAAK,UAAU,SAAS,CAAC;AAAA,IACjD,QAAQ;AAAA,IAER;AACA,WAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAAA,EACvE;AAAA;AAAA,EAGA,UAAU,KAAyC;AACjD,UAAM,QAAQ,KAAK,UAAU,IAAI,GAAG;AACpC,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,KAAK,IAAI,IAAI,MAAM,cAAc,KAAK,OAAO;AAC/C,WAAK,UAAU,OAAO,GAAG;AACzB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAY,KAAkD;AAC5D,UAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;AACnC,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,UAAU,IAAI,QAAwB,CAAC,YAAY;AAEvD,YAAM,QAAQ;AAAA,QACZ,IAAI,QAAwB,CAAC,MAAM;AACjC,gBAAM,OAAO,MAAM;AACnB,gBAAM,UAAU,CAAC,WAAW;AAC1B,iBAAK,MAAM;AACX,oBAAQ,MAAM;AACd,cAAE,MAAM;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAa,KAAmB;AAC9B,SAAK,SAAS,IAAI,KAAK;AAAA,MACrB,SAAS,MAAM;AAAA,MAAC;AAAA,MAChB,SAAS,CAAC;AAAA,IACZ,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,SAAS,KAAa,QAA8B;AAElD,QAAI,OAAO,KAAK,UAAU,eAAe;AACvC,WAAK,UAAU,IAAI,KAAK,MAAM;AAAA,IAChC;AAEA,UAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;AACnC,QAAI,OAAO;AACT,YAAM,QAAQ,MAAM;AACpB,WAAK,SAAS,OAAO,GAAG;AAAA,IAC1B;AAEA,SAAK,MAAM;AAAA,EACb;AAAA;AAAA,EAGA,eAAe,KAAmB;AAChC,SAAK,SAAS,OAAO,GAAG;AAAA,EAC1B;AAAA;AAAA,EAGQ,QAAc;AACpB,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,UAAI,MAAM,MAAM,cAAc,KAAK,OAAO;AACxC,aAAK,UAAU,OAAO,GAAG;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AACF;;;ACzJA,SAAS,oBAAoB,MAAM,gBAAgB;AACnD,SAAS,YAAY;;;ACgEd,IAAM,WAAN,cAAuB,MAAM;AAAA,EACzB,OAAO;AAAA,EACP;AAAA,EAET,YAAY,SAAiB,eAAyB;AACpD,UAAM,cAAc,OAAO,+BAA+B;AAC1D,SAAK,OAAO;AACZ,SAAK,gBAAgB;AAAA,EACvB;AACF;;;ADrEA,IAAMC,aAAY;AAGlB,IAAM,eAAe;AAGd,IAAM,qBAAqB;AAAA;AAAA,EAEhC,oBAAoB;AAAA;AAAA,EAEpB,gBAAgB;AAClB;AAkCO,IAAM,iBAAN,MAAqB;AAAA,EACT;AAAA,EACA;AAAA;AAAA,EAGT,gBAA+B;AAAA;AAAA,EAE/B,WAAW;AAAA,EAEnB,YAAY,eAAuB;AACjC,SAAK,gBAAgB;AACrB,SAAK,SAAS,mBAAmB;AAAA,MAC/B,OAAO;AAAA,MACP,WAAW,KAAK,QAAW;AAAA,QACzB,SAAS;AAAA;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAqC;AACzC,UAAM,MAAM,KAAK,IAAI;AAGrB,QAAI,KAAK,kBAAkB,QAAQ,MAAM,KAAK,WAAW,cAAc;AACrE,aAAO,KAAK,UAAU,KAAK,aAAa;AAAA,IAC1C;AAGA,UAAM,UAAU,MAAM,KAAK,aAAa;AACxC,SAAK,gBAAgB;AACrB,SAAK,WAAW;AAEhB,WAAO,KAAK,UAAU,OAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,gBAAgB,qBAAyD;AAC7E,UAAM,OAAO,MAAM,KAAK,aAAa;AAErC,QAAI,KAAK,WAAW,qBAAqB;AACvC,aAAO,EAAE,YAAY,MAAM,KAAK;AAAA,IAClC;AAEA,UAAM,YAAY,sBAAsB,KAAK;AAC7C,WAAO;AAAA,MACL,YAAY;AAAA,MACZ;AAAA,MACA,WAAW,KAAK,WAAW,SAAS;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAgB,cAA4B;AAC1C,QAAI,KAAK,kBAAkB,QAAQ,KAAK,iBAAiB,cAAc;AACrE,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAmB;AACjB,SAAK,gBAAgB;AACrB,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAgC;AACpC,SAAK,WAAW;AAChB,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,cAA8B;AAEvC,UAAM,UAAU,OAAO,YAAY,IAAI;AACvC,WAAO,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,mBAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAc,eAAgC;AAC5C,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,OAAO,aAAa;AAAA,QAC7C,SAASA;AAAA,QACT,KAAK;AAAA,QACL,cAAc;AAAA,QACd,MAAM,CAAC,KAAK,aAAa;AAAA,MAC3B,CAAC;AACD,aAAO;AAAA,IACT,SAAS,OAAO;AAGd,YAAM,IAAI,SAAS,iBAAiB,QAAQ,MAAM,UAAU,iBAAiB,KAAK;AAAA,IACpF;AAAA,EACF;AAAA;AAAA,EAGQ,UAAU,SAA8B;AAC9C,WAAO;AAAA,MACL;AAAA,MACA,YAAY,KAAK,WAAW,OAAO;AAAA,MACnC,OAAO,UAAU,mBAAmB;AAAA,MACpC,SAAS,UAAU,mBAAmB;AAAA,MACtC,eAAe,KAAK;AAAA,IACtB;AAAA,EACF;AACF;;;AE7LA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,QAAAC,aAAY;AAG9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,QAAQ,UAAU;AAGpC,IAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,MAAMA,SAAQD,MAAK,WAAW,MAAM,cAAc,CAAC;AAElD,IAAM,UAAU,IAAI;AACpB,IAAM,aAAa,cAAc,OAAO;;;ACQxC,IAAM,yBAAwC;AAAA,EACnD,SAAS;AAAA,EACT,WAAW,KAAK,KAAK;AAAA;AAAA,EACrB,YAAY;AACd;AAKO,IAAM,eAAN,MAAmB;AAAA,EAChB,WAAsC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA,kBAAyD;AAAA,EAEjE,YAAY,SAAiC,CAAC,GAAG;AAC/C,SAAK,SAAS,EAAE,GAAG,wBAAwB,GAAG,OAAO;AAGrD,QAAI,KAAK,OAAO,SAAS;AACvB,WAAK,kBAAkB,YAAY,MAAM,KAAK,QAAQ,GAAG,IAAI,KAAK,GAAI;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAA6C;AACtD,QAAI,CAAC,KAAK,OAAO,WAAW,CAAC,WAAW;AACtC,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAGA,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,MAAM,aAAa,KAAK,OAAO,WAAW;AAClD,WAAK,SAAS,OAAO,SAAS;AAC9B,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAAmB,OAAe,MAAoB;AAC/D,QAAI,CAAC,KAAK,OAAO,WAAW,CAAC,WAAW;AACtC;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,SAAS,IAAI,SAAS;AAC5C,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,UAAU;AACZ,eAAS,aAAa;AACtB,eAAS;AAET,UAAI,SAAS,UAAU,OAAO;AAC5B,iBAAS,QAAQ;AACjB,iBAAS,OAAO;AAAA,MAClB;AAAA,IACF,OAAO;AACL,WAAK,SAAS,IAAI,WAAW;AAAA,QAC3B;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,cAAc;AAAA,MAChB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,WAAyB;AACpC,QAAI,CAAC,KAAK,OAAO,WAAW,CAAC,WAAW;AACtC;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,QAAI,OAAO;AACT,YAAM,aAAa,KAAK,IAAI;AAC5B,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,WAAyB;AACpC,SAAK,SAAS,OAAO,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAiB;AACf,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,WAA2F;AACzF,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WAAW,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;AAAA,MACzE,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI;AAAA,MACrB,OAAO,MAAM;AAAA,MACb,KAAK,KAAK,OAAO,MAAM,MAAM,aAAa,GAAI;AAAA,IAChD,EAAE;AACF,WAAO,EAAE,OAAO,KAAK,SAAS,MAAM,SAAS;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAgB;AACtB,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,IAAI,KAAK,KAAK,KAAK,UAAU;AACvC,UAAI,MAAM,MAAM,aAAa,KAAK,OAAO,WAAW;AAClD,aAAK,SAAS,OAAO,EAAE;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,QAAI,KAAK,iBAAiB;AACxB,oBAAc,KAAK,eAAe;AAClC,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AACF;AAKO,SAAS,aACd,SACA,aAAqB,uBAAuB,YACxB;AACpB,QAAM,QAAQ,QAAQ,UAAU,KAAK,QAAQ,WAAW,YAAY,CAAC;AACrE,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GAAG;AAC5C,WAAO,MAAM,CAAC;AAAA,EAChB;AACA,SAAO;AACT;;;Ad9HA,IAAM,eAAe;AACrB,IAAM,aAAa;AACnB,IAAM,mBAAmB;AACzB,IAAM,aAAa;AACnB,IAAM,wBAAwB;AAC9B,IAAM,6BAA6B;AACnC,IAAM,eAAe;AACrB,IAAM,wBAAwB;AAC9B,IAAM,0BAA0B;AAChC,IAAM,yBAAyB;AAC/B,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAM5B,SAAS,sBAAsB,WAA2B;AACxD,MAAI;AAEF,UAAM,SAAS,KAAK,MAAM,SAAS;AAMnC,QAAI,OAAO,UAAU,iCAAiC,OAAO,SAAS;AAGpE,YAAM,QAAQ,OAAO,QAAQ,MAAM,kCAAkC;AACrE,UAAI,OAAO;AACT,cAAM,YAAY,KAAK,MAAM,MAAM,CAAC,CAAC;AAMrC,YAAI,UAAU,kBAAkB,wBAAwB,UAAU,gBAAgB;AAEhF,gBAAM,eAAe,UAAU,eAAe;AAAA,YAC5C;AAAA,UACF;AACA,cAAI,cAAc;AAChB,kBAAM,gBAAgB,SAAS,aAAa,CAAC,GAAG,EAAE;AAClD,kBAAM,iBAAiB,SAAS,aAAa,CAAC,GAAG,EAAE;AACnD,kBAAM,cAAc,gBAAgB,KAAW,QAAQ,CAAC;AACxD,kBAAM,eAAe,iBAAiB,KAAW,QAAQ,CAAC;AAC1D,kBAAM,SAAS,UAAU,SAAS;AAClC,kBAAM,cACJ,OAAO,SAAS,KAAK,GAAG,OAAO,MAAM,GAAG,CAAC,CAAC,MAAM,OAAO,MAAM,EAAE,CAAC,KAAK;AAEvE,mBAAO,KAAK,UAAU;AAAA,cACpB,OAAO;AAAA,gBACL,SAAS,wCAAwC,UAAU,iBAAiB,WAAW;AAAA,gBACvF,MAAM;AAAA,gBACN;AAAA,gBACA,qBAAqB;AAAA,gBACrB,cAAc;AAAA,gBACd,MAAM,eAAe,WAAW;AAAA,cAClC;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAMA,IAAM,oBAAoB,oBAAI,IAAoB;AAKlD,SAAS,cAAc,SAA0B;AAC/C,QAAM,UAAU,kBAAkB,IAAI,OAAO;AAC7C,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,MAAI,WAAW,wBAAwB;AACrC,sBAAkB,OAAO,OAAO;AAChC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,SAAS,gBAAgB,SAAuB;AAC9C,oBAAkB,IAAI,SAAS,KAAK,IAAI,CAAC;AACzC,UAAQ,IAAI,sBAAsB,OAAO,0CAA0C;AACrF;AAKA,SAAS,yBAAyB,QAA4B;AAC5D,QAAM,YAAsB,CAAC;AAC7B,QAAM,cAAwB,CAAC;AAE/B,aAAW,SAAS,QAAQ;AAC1B,QAAI,cAAc,KAAK,GAAG;AACxB,kBAAY,KAAK,KAAK;AAAA,IACxB,OAAO;AACL,gBAAU,KAAK,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,WAAW,GAAG,WAAW;AACtC;AAMA,SAAS,SAAS,KAA8B;AAC9C,SACE,CAAC,IAAI,iBACL,CAAC,IAAI,aACL,IAAI,WAAW,QACf,CAAC,IAAI,OAAO,aACZ,IAAI,OAAO;AAEf;AAMA,SAAS,UAAU,KAAqB,MAAgC;AACtE,MAAI,CAAC,SAAS,GAAG,GAAG;AAClB,WAAO;AAAA,EACT;AACA,SAAO,IAAI,MAAM,IAAI;AACvB;AAMA,IAAM,uBAAuB;AAKtB,SAAS,eAAuB;AACrC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,SAAS;AACX,UAAM,SAAS,SAAS,SAAS,EAAE;AACnC,QAAI,CAAC,MAAM,MAAM,KAAK,SAAS,KAAK,SAAS,OAAO;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAMA,eAAe,mBAAmB,MAA2C;AAC3E,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,uBAAuB;AAE9E,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,oBAAoB,IAAI,WAAW;AAAA,MAC9D,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,iBAAa,SAAS;AAEtB,QAAI,SAAS,IAAI;AACf,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,UAAI,KAAK,WAAW,QAAQ,KAAK,QAAQ;AACvC,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,iBAAa,SAAS;AACtB,WAAO;AAAA,EACT;AACF;AAMA,IAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,IAAM,wBAAwB;AAAA,EAC5B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAKA,SAAS,gBAAgB,QAAgB,MAAuB;AAE9D,MAAI,CAAC,sBAAsB,SAAS,MAAM,GAAG;AAC3C,WAAO;AAAA,EACT;AAGA,MAAI,UAAU,KAAK;AACjB,WAAO;AAAA,EACT;AAGA,SAAO,wBAAwB,KAAK,CAAC,YAAY,QAAQ,KAAK,IAAI,CAAC;AACrE;AAMA,IAAM,cAAc,oBAAI,IAAI,CAAC,UAAU,QAAQ,aAAa,QAAQ,UAAU,CAAC;AAM/E,IAAM,gBAAwC;AAAA,EAC5C,WAAW;AAAA;AAAA,EACX,OAAO;AAAA;AACT;AAQA,IAAM,wBAAwB;AAM9B,SAAS,eAAe,IAA4C;AAClE,MAAI,CAAC,MAAM,OAAO,OAAO,SAAU,QAAO;AAC1C,MAAI,sBAAsB,KAAK,EAAE,EAAG,QAAO;AAG3C,SAAO,GAAG,QAAQ,mBAAmB,GAAG;AAC1C;AAwBA,SAAS,gBAAgB,UAAwC;AAC/D,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO;AAE/C,MAAI,aAAa;AACjB,QAAM,YAAY,SAAS,IAAI,CAAC,QAAQ;AACtC,UAAM,WAAW;AACjB,QAAI,aAAa;AACjB,QAAI,SAAS,EAAE,GAAG,IAAI;AAGtB,QAAI,SAAS,cAAc,MAAM,QAAQ,SAAS,UAAU,GAAG;AAC7D,YAAM,eAAe,SAAS,WAAW,IAAI,CAAC,OAAO;AACnD,YAAI,GAAG,MAAM,OAAO,GAAG,OAAO,UAAU;AACtC,gBAAME,aAAY,eAAe,GAAG,EAAE;AACtC,cAAIA,eAAc,GAAG,IAAI;AACvB,yBAAa;AACb,mBAAO,EAAE,GAAG,IAAI,IAAIA,WAAU;AAAA,UAChC;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AACD,UAAI,YAAY;AACd,iBAAS,EAAE,GAAG,QAAQ,YAAY,aAAa;AAAA,MACjD;AAAA,IACF;AAGA,QAAI,SAAS,gBAAgB,OAAO,SAAS,iBAAiB,UAAU;AACtE,YAAMA,aAAY,eAAe,SAAS,YAAY;AACtD,UAAIA,eAAc,SAAS,cAAc;AACvC,qBAAa;AACb,iBAAS,EAAE,GAAG,QAAQ,cAAcA,WAAU;AAAA,MAChD;AAAA,IACF;AAGA,QAAI,MAAM,QAAQ,SAAS,OAAO,GAAG;AACnC,YAAM,aAAc,SAAS,QAA2B,IAAI,CAAC,UAAU;AACrE,YAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAEhD,YAAI,eAAe;AACnB,YAAI,WAAW,EAAE,GAAG,MAAM;AAG1B,YAAI,MAAM,SAAS,cAAc,MAAM,MAAM,OAAO,MAAM,OAAO,UAAU;AACzE,gBAAMA,aAAY,eAAe,MAAM,EAAE;AACzC,cAAIA,eAAc,MAAM,IAAI;AAC1B,2BAAe;AACf,uBAAW,EAAE,GAAG,UAAU,IAAIA,WAAU;AAAA,UAC1C;AAAA,QACF;AAGA,YACE,MAAM,SAAS,iBACf,MAAM,eACN,OAAO,MAAM,gBAAgB,UAC7B;AACA,gBAAMA,aAAY,eAAe,MAAM,WAAW;AAClD,cAAIA,eAAc,MAAM,aAAa;AACnC,2BAAe;AACf,uBAAW,EAAE,GAAG,UAAU,aAAaA,WAAU;AAAA,UACnD;AAAA,QACF;AAEA,YAAI,cAAc;AAChB,uBAAa;AACb,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT,CAAC;AAED,UAAI,YAAY;AACd,iBAAS,EAAE,GAAG,QAAQ,SAAS,WAAW;AAAA,MAC5C;AAAA,IACF;AAEA,QAAI,YAAY;AACd,mBAAa;AACb,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,CAAC;AAED,SAAO,aAAa,YAAY;AAClC;AAMA,SAAS,sBAAsB,UAAwC;AACrE,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO;AAE/C,MAAI,aAAa;AACjB,QAAM,aAAa,SAAS,IAAI,CAAC,QAAQ;AACvC,QAAI,YAAY,IAAI,IAAI,IAAI,EAAG,QAAO;AAEtC,UAAM,aAAa,cAAc,IAAI,IAAI;AACzC,QAAI,YAAY;AACd,mBAAa;AACb,aAAO,EAAE,GAAG,KAAK,MAAM,WAAW;AAAA,IACpC;AAGA,iBAAa;AACb,WAAO,EAAE,GAAG,KAAK,MAAM,OAAO;AAAA,EAChC,CAAC;AAED,SAAO,aAAa,aAAa;AACnC;AAQA,SAAS,2BAA2B,UAAwC;AAC1E,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO;AAG/C,MAAI,oBAAoB;AACxB,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,QAAI,SAAS,CAAC,EAAE,SAAS,UAAU;AACjC,0BAAoB;AACpB;AAAA,IACF;AAAA,EACF;AAGA,MAAI,sBAAsB,GAAI,QAAO;AAErC,QAAM,YAAY,SAAS,iBAAiB,EAAE;AAG9C,MAAI,cAAc,OAAQ,QAAO;AAGjC,MAAI,cAAc,eAAe,cAAc,SAAS;AACtD,UAAM,aAAa,CAAC,GAAG,QAAQ;AAC/B,eAAW,OAAO,mBAAmB,GAAG;AAAA,MACtC,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AACD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKA,SAAS,cAAc,SAA0B;AAC/C,SAAO,QAAQ,WAAW,SAAS,KAAK,QAAQ,WAAW,QAAQ;AACrE;AAgBA,SAAS,6BAA6B,UAAwD;AAC5F,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO;AAE/C,MAAI,aAAa;AACjB,QAAM,aAAa,SAAS,IAAI,CAAC,QAAQ;AAEvC,QAAI,IAAI,SAAS,eAAe,IAAI,sBAAsB,QAAW;AACnE,aAAO;AAAA,IACT;AAGA,UAAM,qBACJ,IAAI,cAAc,MAAM,QAAQ,IAAI,UAAU,KAAK,IAAI,WAAW,SAAS;AAG7E,UAAM,sBACJ,MAAM,QAAQ,IAAI,OAAO,KACxB,IAAI,QAAqC,KAAK,CAAC,UAAU,OAAO,SAAS,UAAU;AAEtF,QAAI,sBAAsB,qBAAqB;AAC7C,mBAAa;AACb,aAAO,EAAE,GAAG,KAAK,mBAAmB,GAAG;AAAA,IACzC;AACA,WAAO;AAAA,EACT,CAAC;AAED,SAAO,aAAa,aAAa;AACnC;AAOA,IAAM,gBAAgB;AAGtB,IAAM,gBAAgB;AAGtB,IAAM,kBAAkB;AAGxB,IAAM,oBACJ;AASF,SAAS,oBAAoB,SAAyB;AACpD,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI,UAAU,QAAQ,QAAQ,eAAe,EAAE;AAE/C,YAAU,QAAQ,QAAQ,eAAe,EAAE;AAE3C,YAAU,QAAQ,QAAQ,mBAAmB,EAAE;AAE/C,YAAU,QAAQ,QAAQ,iBAAiB,EAAE;AAC7C,SAAO;AACT;AAmDA,SAAS,oBAA+C;AACtD,QAAM,MAAM,oBAAI,IAA0B;AAC1C,aAAW,KAAK,iBAAiB;AAC/B,QAAI,EAAE,OAAO,WAAY;AACzB,QAAI,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,aAAa,EAAE,YAAY,CAAC;AAAA,EACxE;AACA,SAAO;AACT;AAKA,SAAS,mBAAmB,WAAmD;AAC7E,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,YAAY,EAAE,GAAG,uBAAuB,YAAY,GAAG,UAAU,WAAW;AAAA,IAC5E,SAAS,EAAE,GAAG,uBAAuB,SAAS,GAAG,UAAU,QAAQ;AAAA,IACnE,OAAO,EAAE,GAAG,uBAAuB,OAAO,GAAG,UAAU,MAAM;AAAA,IAC7D,WAAW,EAAE,GAAG,uBAAuB,WAAW,GAAG,UAAU,UAAU;AAAA,EAC3E;AACF;AAMA,SAAS,eACP,SACA,YACA,WACoB;AACpB,QAAM,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AAC1D,MAAI,CAAC,MAAO,QAAO;AAGnB,QAAM,uBAAuB,KAAK,KAAK,aAAa,CAAC;AACrD,QAAM,wBAAwB,aAAa,MAAM,aAAa;AAE9D,QAAM,UACH,uBAAuB,MAAa,MAAM,aAC1C,wBAAwB,MAAa,MAAM;AAI9C,QAAM,eAAe,KAAK,IAAI,KAAK,KAAK,KAAK,UAAU,MAAM,GAAS,CAAC;AACvE,SAAO,aAAa,SAAS;AAC/B;AAUA,eAAsB,WAAW,SAA6C;AAC5E,QAAM,UAAU,QAAQ,WAAW;AAGnC,QAAM,aAAa,QAAQ,QAAQ,aAAa;AAGhD,QAAM,iBAAiB,MAAM,mBAAmB,UAAU;AAC1D,MAAI,gBAAgB;AAElB,UAAMC,WAAUC,qBAAoB,QAAQ,SAA0B;AACtE,UAAMC,kBAAiB,IAAI,eAAeF,SAAQ,OAAO;AACzD,UAAMG,WAAU,oBAAoB,UAAU;AAG9C,QAAI,mBAAmBH,SAAQ,SAAS;AACtC,cAAQ;AAAA,QACN,uCAAuC,UAAU,gBAAgB,cAAc,6BAA6BA,SAAQ,OAAO;AAAA,MAC7H;AAAA,IACF;AAEA,YAAQ,UAAU,UAAU;AAE5B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAAG;AAAA,MACA,eAAe;AAAA,MACf,gBAAAD;AAAA,MACA,OAAO,YAAY;AAAA,MAEnB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,UAAUD,qBAAoB,QAAQ,SAA0B;AACtE,QAAM,EAAE,OAAO,SAAS,IAAI,mBAAmB,QAAQ,SAA0B;AAGjF,QAAM,iBAAiB,IAAI,eAAe,QAAQ,OAAO;AAGzD,QAAM,gBAAgB,mBAAmB,QAAQ,aAAa;AAC9D,QAAM,eAAe,kBAAkB;AACvC,QAAM,aAA4B;AAAA,IAChC,QAAQ;AAAA,IACR;AAAA,EACF;AAGA,QAAM,eAAe,IAAI,oBAAoB;AAG7C,QAAM,eAAe,IAAI,aAAa,QAAQ,aAAa;AAG3D,QAAM,cAAc,oBAAI,IAA0B;AAElD,QAAM,SAAS,aAAa,OAAO,KAAsB,QAAwB;AAE/E,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,cAAQ,MAAM,sCAAsC,IAAI,OAAO,EAAE;AAAA,IAEnE,CAAC;AAED,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,cAAQ,MAAM,uCAAuC,IAAI,OAAO,EAAE;AAAA,IAEpE,CAAC;AAGD,aAAS,KAAK,CAAC,QAAQ;AACrB,UAAI,OAAO,IAAI,SAAS,wBAAwB;AAC9C,gBAAQ,MAAM,8CAA8C,IAAI,OAAO,EAAE;AAAA,MAC3E;AAAA,IAGF,CAAC;AAGD,aAAS,KAAK,CAAC,QAAQ;AACrB,UAAI,OAAO,IAAI,SAAS,wBAAwB;AAC9C,gBAAQ,MAAM,6CAA6C,IAAI,OAAO,EAAE;AAAA,MAC1E;AAAA,IACF,CAAC;AAGD,QAAI,IAAI,QAAQ,aAAa,IAAI,KAAK,WAAW,UAAU,GAAG;AAC5D,YAAM,MAAM,IAAI,IAAI,IAAI,KAAK,kBAAkB;AAC/C,YAAM,OAAO,IAAI,aAAa,IAAI,MAAM,MAAM;AAE9C,YAAM,WAAoC;AAAA,QACxC,QAAQ;AAAA,QACR,QAAQ,QAAQ;AAAA,MAClB;AAEA,UAAI,MAAM;AACR,YAAI;AACF,gBAAM,cAAc,MAAM,eAAe,aAAa;AACtD,mBAAS,UAAU,YAAY;AAC/B,mBAAS,QAAQ,YAAY;AAC7B,mBAAS,UAAU,YAAY;AAAA,QACjC,QAAQ;AACN,mBAAS,eAAe;AAAA,QAC1B;AAAA,MACF;AAEA,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,QAAQ,CAAC;AAChC;AAAA,IACF;AAGA,QAAI,IAAI,QAAQ,YAAY,IAAI,KAAK,WAAW,SAAS,GAAG;AAC1D,UAAI;AACF,cAAM,MAAM,IAAI,IAAI,IAAI,KAAK,kBAAkB;AAC/C,cAAM,OAAO,SAAS,IAAI,aAAa,IAAI,MAAM,KAAK,KAAK,EAAE;AAC7D,cAAM,QAAQ,MAAM,SAAS,KAAK,IAAI,MAAM,EAAE,CAAC;AAE/C,YAAI,UAAU,KAAK;AAAA,UACjB,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,QACnB,CAAC;AACD,YAAI,IAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,MACxC,SAAS,KAAK;AACZ,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI;AAAA,UACF,KAAK,UAAU;AAAA,YACb,OAAO,wBAAwB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,UACjF,CAAC;AAAA,QACH;AAAA,MACF;AACA;AAAA,IACF;AAGA,QAAI,IAAI,QAAQ,gBAAgB,IAAI,WAAW,OAAO;AACpD,YAAM,SAAS,gBAAgB,OAAO,CAAC,MAAM,EAAE,OAAO,eAAe,EAAE,IAAI,CAAC,OAAO;AAAA,QACjF,IAAI,EAAE;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,QACrC,UAAU,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK;AAAA,MAClC,EAAE;AACF,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,QAAQ,MAAM,OAAO,CAAC,CAAC;AACxD;AAAA,IACF;AAGA,QAAI,CAAC,IAAI,KAAK,WAAW,KAAK,GAAG;AAC/B,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,YAAY,CAAC,CAAC;AAC9C;AAAA,IACF;AAEA,QAAI;AACF,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,cAAQ,UAAU,KAAK;AAEvB,UAAI,CAAC,IAAI,aAAa;AACpB,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI;AAAA,UACF,KAAK,UAAU;AAAA,YACb,OAAO,EAAE,SAAS,gBAAgB,MAAM,OAAO,IAAI,MAAM,cAAc;AAAA,UACzE,CAAC;AAAA,QACH;AAAA,MACF,WAAW,CAAC,IAAI,eAAe;AAE7B,YAAI;AAAA,UACF,SAAS,KAAK,UAAU,EAAE,OAAO,EAAE,SAAS,MAAM,SAAS,MAAM,cAAc,EAAE,CAAC,CAAC;AAAA;AAAA;AAAA,QACrF;AACA,YAAI,MAAM,kBAAkB;AAC5B,YAAI,IAAI;AAAA,MACV;AAAA,IACF;AAAA,EACF,CAAC;AAKD,QAAM,YAAY,CAAC,YAAmC;AACpD,WAAO,IAAI,QAAc,CAAC,gBAAgB,kBAAkB;AAC1D,YAAM,UAAU,OAAO,QAA+B;AACpD,eAAO,eAAe,SAAS,OAAO;AAEtC,YAAI,IAAI,SAAS,cAAc;AAE7B,gBAAMG,kBAAiB,MAAM,mBAAmB,UAAU;AAC1D,cAAIA,iBAAgB;AAElB,oBAAQ,IAAI,gDAAgD,UAAU,WAAW;AACjF,0BAAc,EAAE,MAAM,kBAAkB,QAAQA,gBAAe,CAAC;AAChE;AAAA,UACF;AAGA,cAAI,UAAU,qBAAqB;AACjC,oBAAQ;AAAA,cACN,qBAAqB,UAAU,8BAA8B,mBAAmB,eAAe,OAAO,IAAI,mBAAmB;AAAA,YAC/H;AACA,0BAAc,EAAE,MAAM,SAAS,QAAQ,CAAC;AACxC;AAAA,UACF;AAGA,kBAAQ;AAAA,YACN,qBAAqB,UAAU,uBAAuB,mBAAmB;AAAA,UAC3E;AACA,wBAAc,GAAG;AACjB;AAAA,QACF;AAEA,sBAAc,GAAG;AAAA,MACnB;AAEA,aAAO,KAAK,SAAS,OAAO;AAC5B,aAAO,OAAO,YAAY,aAAa,MAAM;AAC3C,eAAO,eAAe,SAAS,OAAO;AACtC,uBAAe;AAAA,MACjB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAGA,MAAI;AACJ,WAAS,UAAU,GAAG,WAAW,qBAAqB,WAAW;AAC/D,QAAI;AACF,YAAM,UAAU,OAAO;AACvB;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,QAAQ;AAEd,UAAI,MAAM,SAAS,oBAAoB,MAAM,QAAQ;AAEnD,cAAMD,WAAU,oBAAoB,UAAU;AAC9C,gBAAQ,UAAU,UAAU;AAC5B,eAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAAA;AAAA,UACA,eAAe,MAAM;AAAA,UACrB;AAAA,UACA,OAAO,YAAY;AAAA,UAEnB;AAAA,QACF;AAAA,MACF;AAEA,UAAI,MAAM,SAAS,SAAS;AAE1B,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAC3D;AAAA,MACF;AAGA,kBAAY;AACZ;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW;AACb,UAAM;AAAA,EACR;AAGA,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,OAAO,KAAK;AAClB,QAAM,UAAU,oBAAoB,IAAI;AAExC,UAAQ,UAAU,IAAI;AAItB,SAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,YAAQ,MAAM,sCAAsC,IAAI,OAAO,EAAE;AACjE,YAAQ,UAAU,GAAG;AAAA,EAEvB,CAAC;AAGD,SAAO,GAAG,eAAe,CAAC,KAAK,WAAW;AACxC,YAAQ,MAAM,8BAA8B,IAAI,OAAO,EAAE;AAEzD,QAAI,OAAO,YAAY,CAAC,OAAO,WAAW;AACxC,aAAO,IAAI,kCAAkC;AAAA,IAC/C;AAAA,EACF,CAAC;AAGD,SAAO,GAAG,cAAc,CAAC,WAAW;AAClC,gBAAY,IAAI,MAAM;AAGtB,WAAO,WAAW,GAAO;AAEzB,WAAO,GAAG,WAAW,MAAM;AACzB,cAAQ,MAAM,oDAAoD;AAClE,aAAO,QAAQ;AAAA,IACjB,CAAC;AAED,WAAO,GAAG,OAAO,MAAM;AAAA,IAEvB,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,cAAQ,MAAM,8BAA8B,IAAI,OAAO,EAAE;AAAA,IAC3D,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AACvB,kBAAY,OAAO,MAAM;AAAA,IAC3B,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe,QAAQ;AAAA,IACvB;AAAA,IACA,OAAO,MACL,IAAI,QAAc,CAAC,KAAK,QAAQ;AAC9B,YAAM,UAAU,WAAW,MAAM;AAC/B,YAAI,IAAI,MAAM,qCAAqC,CAAC;AAAA,MACtD,GAAG,GAAI;AAEP,mBAAa,MAAM;AAEnB,iBAAW,UAAU,aAAa;AAChC,eAAO,QAAQ;AAAA,MACjB;AACA,kBAAY,MAAM;AAClB,aAAO,MAAM,CAAC,QAAQ;AACpB,qBAAa,OAAO;AACpB,YAAI,KAAK;AACP,cAAI,GAAG;AAAA,QACT,OAAO;AACL,cAAI;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACL;AACF;AAeA,eAAe,gBACb,aACA,QACA,SACA,MACA,SACA,WACA,UAKA,gBACA,QAC6B;AAE7B,MAAI,cAAc;AAClB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,KAAK,SAAS,CAAC;AACzC,WAAO,QAAQ;AAGf,QAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG;AAClC,aAAO,WAAW,sBAAsB,OAAO,QAAyB;AAAA,IAC1E;AAGA,QAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG;AAClC,aAAO,WAAW,gBAAgB,OAAO,QAAyB;AAAA,IACpE;AAGA,QAAI,cAAc,OAAO,KAAK,MAAM,QAAQ,OAAO,QAAQ,GAAG;AAC5D,aAAO,WAAW,2BAA2B,OAAO,QAAyB;AAAA,IAC/E;AAIA,UAAM,qBAAqB,CAAC,EAC1B,OAAO,YACP,OAAO,qBACP,iBAAiB,OAAO;AAE1B,QAAI,sBAAsB,MAAM,QAAQ,OAAO,QAAQ,GAAG;AACxD,aAAO,WAAW,6BAA6B,OAAO,QAAiC;AAAA,IACzF;AAEA,kBAAc,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC;AAAA,EAClD,QAAQ;AAAA,EAER;AAGA,QAAM,YAAY,eAAe,SAAS,YAAY,QAAQ,SAAS;AACvE,QAAM,UAAqC,YAAY,EAAE,iBAAiB,UAAU,IAAI;AAExF,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA,MAAM,YAAY,SAAS,IAAI,IAAI,WAAW,WAAW,IAAI;AAAA,QAC7D;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAGA,QAAI,SAAS,WAAW,KAAK;AAE3B,YAAM,YAAY,MAAM,SAAS,KAAK;AACtC,YAAM,gBAAgB,gBAAgB,SAAS,QAAQ,SAAS;AAEhE,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,aAAa,SAAS;AAAA,QACtB,iBAAiB;AAAA,MACnB;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,MAAM,SAAS;AAAA,EACnC,SAAS,KAAK;AACZ,UAAM,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAChE,WAAO;AAAA,MACL,SAAS;AAAA,MACT,WAAW;AAAA,MACX,aAAa;AAAA,MACb,iBAAiB;AAAA;AAAA,IACnB;AAAA,EACF;AACF;AAYA,eAAe,aACb,KACA,KACA,SACA,UAKA,SACA,YACA,cACA,gBACA,cACe;AACf,QAAM,YAAY,KAAK,IAAI;AAG3B,QAAM,cAAc,GAAG,OAAO,GAAG,IAAI,GAAG;AAGxC,QAAM,aAAuB,CAAC;AAC9B,mBAAiB,SAAS,KAAK;AAC7B,eAAW,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC;AAAA,EACrE;AACA,MAAI,OAAO,OAAO,OAAO,UAAU;AAGnC,MAAI;AACJ,MAAI,cAAc;AAClB,MAAI,UAAU;AACd,MAAI,YAAY;AAChB,QAAM,mBAAmB,IAAI,KAAK,SAAS,mBAAmB;AAE9D,MAAI,oBAAoB,KAAK,SAAS,GAAG;AACvC,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK,SAAS,CAAC;AACzC,oBAAc,OAAO,WAAW;AAChC,gBAAW,OAAO,SAAoB;AACtC,kBAAa,OAAO,cAAyB;AAI7C,UAAI,eAAe;AACnB,UAAI,OAAO,WAAW,MAAM;AAC1B,eAAO,SAAS;AAChB,uBAAe;AAAA,MACjB;AAGA,YAAM,kBACJ,OAAO,OAAO,UAAU,WAAW,OAAO,MAAM,KAAK,EAAE,YAAY,IAAI;AAGzE,YAAM,gBAAgB,kBAAkB,eAAe;AACvD,YAAM,WAAW,kBAAkB;AAEnC,YAAM,cACJ,oBAAoB,WAAW,YAAY,KAC3C,oBAAoB,iBAAiB,YAAY;AAGnD,cAAQ;AAAA,QACN,iCAAiC,OAAO,KAAK,qBAAqB,eAAe,IAAI,WAAW,eAAe,aAAa,MAAM,EAAE,aAAa,WAAW;AAAA,MAC9J;AAGA,UAAI,YAAY,CAAC,aAAa;AAC5B,eAAO,QAAQ;AACf,kBAAU;AACV,uBAAe;AAAA,MACjB;AAEA,UAAI,aAAa;AAEf,cAAM,YAAY;AAAA,UAChB,IAAI;AAAA,QACN;AACA,cAAM,kBAAkB,YAAY,aAAa,WAAW,SAAS,IAAI;AAEzE,YAAI,iBAAiB;AAEnB,kBAAQ;AAAA,YACN,wBAAwB,WAAW,MAAM,GAAG,CAAC,CAAC,2BAA2B,gBAAgB,KAAK;AAAA,UAChG;AACA,iBAAO,QAAQ,gBAAgB;AAC/B,oBAAU,gBAAgB;AAC1B,yBAAe;AACf,uBAAa,aAAa,SAAU;AAAA,QACtC,OAAO;AAIL,gBAAM,WAAW,OAAO;AACxB,cAAI;AACJ,cAAI,UAAU;AACZ,qBAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,kBAAI,SAAS,CAAC,EAAE,SAAS,QAAQ;AAC/B,8BAAc,SAAS,CAAC;AACxB;AAAA,cACF;AAAA,YACF;AAAA,UACF;AACA,gBAAM,YAAY,UAAU,KAAK,CAAC,MAAmB,EAAE,SAAS,QAAQ;AACxE,gBAAM,SAAS,OAAO,aAAa,YAAY,WAAW,YAAY,UAAU;AAChF,gBAAM,eACJ,OAAO,WAAW,YAAY,WAAW,UAAU,UAAU;AAK/D,gBAAM,QAAQ,OAAO;AACrB,gBAAM,WAAW,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS;AAExD,cAAI,UAAU;AACZ,oBAAQ,IAAI,gCAAgC,MAAM,MAAM,8BAA8B;AAAA,UACxF;AAEA,4BAAkB,MAAM,QAAQ,cAAc,WAAW,UAAU;AAGnE,iBAAO,QAAQ,gBAAgB;AAC/B,oBAAU,gBAAgB;AAC1B,yBAAe;AAGf,cAAI,WAAW;AACb,yBAAa,WAAW,WAAW,gBAAgB,OAAO,gBAAgB,IAAI;AAC9E,oBAAQ;AAAA,cACN,wBAAwB,UAAU,MAAM,GAAG,CAAC,CAAC,wBAAwB,gBAAgB,KAAK;AAAA,YAC5F;AAAA,UACF;AAEA,kBAAQ,WAAW,eAAe;AAAA,QACpC;AAAA,MACF;AAGA,UAAI,cAAc;AAChB,eAAO,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC;AAAA,MAC3C;AAAA,IACF,SAAS,KAAK;AAEZ,YAAM,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAChE,cAAQ,MAAM,+BAA+B,QAAQ,EAAE;AACvD,cAAQ,UAAU,IAAI,MAAM,mBAAmB,QAAQ,EAAE,CAAC;AAAA,IAC5D;AAAA,EACF;AAGA,QAAM,WAAW,oBAAoB,KAAK,IAAI;AAG9C,QAAM,SAAS,aAAa,UAAU,QAAQ;AAC9C,MAAI,QAAQ;AACV,QAAI,UAAU,OAAO,QAAQ,OAAO,OAAO;AAC3C,QAAI,IAAI,OAAO,IAAI;AACnB;AAAA,EACF;AAGA,QAAM,WAAW,aAAa,YAAY,QAAQ;AAClD,MAAI,UAAU;AACZ,UAAM,SAAS,MAAM;AACrB,QAAI,UAAU,OAAO,QAAQ,OAAO,OAAO;AAC3C,QAAI,IAAI,OAAO,IAAI;AACnB;AAAA,EACF;AAGA,eAAa,aAAa,QAAQ;AAKlC,MAAI;AACJ,QAAM,cAAc,YAAY;AAEhC,MAAI,WAAW,CAAC,QAAQ,oBAAoB,CAAC,aAAa;AACxD,UAAM,YAAY,eAAe,SAAS,KAAK,QAAQ,SAAS;AAChE,QAAI,WAAW;AACb,4BAAsB,OAAO,SAAS;AAItC,YAAM,qBACH,sBAAsB,OAAO,KAAK,KAAK,uBAAuB,GAAG,CAAC,IAAK;AAG1E,YAAM,cAAc,MAAM,eAAe,gBAAgB,kBAAkB;AAE3E,UAAI,YAAY,KAAK,WAAW,CAAC,YAAY,YAAY;AAGvD,cAAM,gBAAgB;AACtB,gBAAQ;AAAA,UACN,uBAAuB,YAAY,KAAK,UAAU,UAAU,cAAc,MAAM,YAAY,KAAK,UAAU,kCAAkC,UAAU,gBAAgB,aAAa;AAAA,QACtL;AACA,kBAAU;AAEV,cAAM,SAAS,KAAK,MAAM,KAAK,SAAS,CAAC;AACzC,eAAO,QAAQ;AACf,eAAO,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC;AAGzC,gBAAQ,eAAe;AAAA,UACrB,YAAY,YAAY,KAAK;AAAA,UAC7B,eAAe,YAAY,KAAK;AAAA,QAClC,CAAC;AAAA,MACH,WAAW,YAAY,KAAK,OAAO;AAEjC,gBAAQ,eAAe;AAAA,UACrB,YAAY,YAAY,KAAK;AAAA,UAC7B,eAAe,YAAY,KAAK;AAAA,QAClC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AACJ,MAAI,mBAAmB;AAEvB,MAAI,aAAa;AAEf,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AACD,uBAAmB;AAGnB,cAAU,KAAK,iBAAiB;AAGhC,wBAAoB,YAAY,MAAM;AACpC,UAAI,SAAS,GAAG,GAAG;AACjB,kBAAU,KAAK,iBAAiB;AAAA,MAClC,OAAO;AAEL,sBAAc,iBAAiB;AAC/B,4BAAoB;AAAA,MACtB;AAAA,IACF,GAAG,qBAAqB;AAAA,EAC1B;AAGA,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,OAAO,GAAG;AACtD,QACE,QAAQ,UACR,QAAQ,gBACR,QAAQ,uBACR,QAAQ;AAER;AACF,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAQ,GAAG,IAAI;AAAA,IACjB;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,cAAc,GAAG;AAC5B,YAAQ,cAAc,IAAI;AAAA,EAC5B;AACA,UAAQ,YAAY,IAAI;AAGxB,MAAI,YAAY;AAChB,MAAI,GAAG,SAAS,MAAM;AACpB,QAAI,mBAAmB;AACrB,oBAAc,iBAAiB;AAC/B,0BAAoB;AAAA,IACtB;AAEA,QAAI,CAAC,WAAW;AACd,mBAAa,eAAe,QAAQ;AAAA,IACtC;AAAA,EACF,CAAC;AAGD,QAAM,YAAY,QAAQ,oBAAoB;AAC9C,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAEhE,MAAI;AAIF,QAAI;AACJ,QAAI,iBAAiB;AAEnB,YAAM,uBAAuB,KAAK,KAAK,KAAK,SAAS,CAAC;AACtD,YAAM,uBAAuB,uBAAuB;AAGpD,YAAM,kBACJ,gBAAgB,WAAW,SAAS,SAAS,KAAK,WAAW,OAAO;AACtE,YAAM,cAAc,kBAChB,WAAW,OAAO,eAClB,WAAW,OAAO;AAGtB,YAAM,YAAY,iBAAiB,gBAAgB,MAAM,WAAW;AACpE,YAAM,kBAAkB;AAAA,QACtB,gBAAgB;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAGA,YAAM,kBAAkB,UAAU,OAAO,CAAC,MAAM,CAAC,gBAAgB,SAAS,CAAC,CAAC;AAC5E,UAAI,gBAAgB,SAAS,GAAG;AAC9B,gBAAQ;AAAA,UACN,iCAAiC,oBAAoB,sBAAsB,gBAAgB,KAAK,IAAI,CAAC;AAAA,QACvG;AAAA,MACF;AAGA,oBAAc,gBAAgB,MAAM,GAAG,qBAAqB;AAG5D,oBAAc,yBAAyB,WAAW;AAAA,IACpD,OAAO;AAGL,UAAI,WAAW,YAAY,YAAY;AACrC,sBAAc,CAAC,SAAS,UAAU;AAAA,MACpC,OAAO;AACL,sBAAc,UAAU,CAAC,OAAO,IAAI,CAAC;AAAA,MACvC;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACJ,QAAI,kBAAkB;AAEtB,aAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,YAAM,WAAW,YAAY,CAAC;AAC9B,YAAM,gBAAgB,MAAM,YAAY,SAAS;AAEjD,cAAQ,IAAI,6BAA6B,IAAI,CAAC,IAAI,YAAY,MAAM,KAAK,QAAQ,EAAE;AAEnF,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA,IAAI,UAAU;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACb;AAEA,UAAI,OAAO,WAAW,OAAO,UAAU;AACrC,mBAAW,OAAO;AAClB,0BAAkB;AAClB,gBAAQ,IAAI,oCAAoC,QAAQ,EAAE;AAC1D;AAAA,MACF;AAGA,kBAAY;AAAA,QACV,MAAM,OAAO,aAAa;AAAA,QAC1B,QAAQ,OAAO,eAAe;AAAA,MAChC;AAGA,UAAI,OAAO,mBAAmB,CAAC,eAAe;AAE5C,YAAI,OAAO,gBAAgB,KAAK;AAC9B,0BAAgB,QAAQ;AAAA,QAC1B;AACA,gBAAQ;AAAA,UACN,oCAAoC,QAAQ,sBAAsB,OAAO,WAAW,MAAM,GAAG,GAAG,CAAC;AAAA,QACnG;AACA;AAAA,MACF;AAGA,UAAI,CAAC,OAAO,iBAAiB;AAC3B,gBAAQ;AAAA,UACN,wCAAwC,QAAQ,mBAAmB,OAAO,WAAW,MAAM,GAAG,GAAG,CAAC;AAAA,QACpG;AAAA,MACF;AACA;AAAA,IACF;AAGA,iBAAa,SAAS;AAGtB,QAAI,mBAAmB;AACrB,oBAAc,iBAAiB;AAC/B,0BAAoB;AAAA,IACtB;AAIA,QAAI,mBAAmB,oBAAoB,gBAAgB,OAAO;AAChE,YAAM,uBAAuB,KAAK,KAAK,KAAK,SAAS,CAAC;AACtD,YAAM,WAAW;AAAA,QACf;AAAA,QACA,WAAW;AAAA,QACX;AAAA,QACA;AAAA,MACF;AACA,wBAAkB;AAAA,QAChB,GAAG;AAAA,QACH,OAAO;AAAA,QACP,WAAW,GAAG,gBAAgB,SAAS,kBAAkB,eAAe;AAAA,QACxE,cAAc,SAAS;AAAA,QACvB,cAAc,SAAS;AAAA,QACvB,SAAS,SAAS;AAAA,MACpB;AACA,cAAQ,WAAW,eAAe;AAAA,IACpC;AAGA,QAAI,CAAC,UAAU;AACb,YAAM,aAAa,WAAW,QAAQ;AACtC,YAAM,YAAY,WAAW,UAAU;AAGvC,YAAM,iBAAiB,sBAAsB,UAAU;AAEvD,UAAI,kBAAkB;AAGpB,YAAI;AACJ,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,cAAc;AACxC,uBAAa,KAAK,UAAU,MAAM;AAAA,QACpC,QAAQ;AACN,uBAAa,KAAK,UAAU;AAAA,YAC1B,OAAO,EAAE,SAAS,YAAY,MAAM,kBAAkB,QAAQ,UAAU;AAAA,UAC1E,CAAC;AAAA,QACH;AACA,cAAM,WAAW,SAAS,UAAU;AAAA;AAAA;AACpC,kBAAU,KAAK,QAAQ;AACvB,kBAAU,KAAK,kBAAkB;AACjC,YAAI,IAAI;AAER,cAAM,SAAS,OAAO,KAAK,WAAW,kBAAkB;AACxD,qBAAa,SAAS,UAAU;AAAA,UAC9B,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB;AAAA,UAC/C,MAAM;AAAA,UACN,aAAa,KAAK,IAAI;AAAA,QACxB,CAAC;AAAA,MACH,OAAO;AAEL,YAAI,UAAU,WAAW,EAAE,gBAAgB,mBAAmB,CAAC;AAC/D,YAAI,IAAI,cAAc;AAEtB,qBAAa,SAAS,UAAU;AAAA,UAC9B,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,OAAO,KAAK,cAAc;AAAA,UAChC,aAAa,KAAK,IAAI;AAAA,QACxB,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAGA,UAAM,iBAA2B,CAAC;AAElC,QAAI,kBAAkB;AAQpB,UAAI,SAAS,MAAM;AACjB,cAAM,SAAS,SAAS,KAAK,UAAU;AACvC,cAAM,SAAuB,CAAC;AAC9B,YAAI;AACF,iBAAO,MAAM;AACX,kBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,gBAAI,KAAM;AACV,mBAAO,KAAK,KAAK;AAAA,UACnB;AAAA,QACF,UAAE;AACA,iBAAO,YAAY;AAAA,QACrB;AAGA,cAAM,WAAW,OAAO,OAAO,MAAM;AACrC,cAAM,UAAU,SAAS,SAAS;AAClC,YAAI;AACF,gBAAM,MAAM,KAAK,MAAM,OAAO;AAgC9B,gBAAM,YAAY;AAAA,YAChB,IAAI,IAAI,MAAM,YAAY,KAAK,IAAI,CAAC;AAAA,YACpC,QAAQ;AAAA,YACR,SAAS,IAAI,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,YACpD,OAAO,IAAI,SAAS;AAAA,YACpB,oBAAoB;AAAA,UACtB;AAGA,cAAI,IAAI,WAAW,MAAM,QAAQ,IAAI,OAAO,GAAG;AAC7C,uBAAW,UAAU,IAAI,SAAS;AAEhC,oBAAM,aAAa,OAAO,SAAS,WAAW,OAAO,OAAO,WAAW;AACvE,oBAAM,UAAU,oBAAoB,UAAU;AAC9C,oBAAM,OAAO,OAAO,SAAS,QAAQ,OAAO,OAAO,QAAQ;AAC3D,oBAAM,QAAQ,OAAO,SAAS;AAG9B,oBAAM,YAAY;AAAA,gBAChB,GAAG;AAAA,gBACH,SAAS,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,UAAU,MAAM,eAAe,KAAK,CAAC;AAAA,cAC3E;AACA,oBAAM,WAAW,SAAS,KAAK,UAAU,SAAS,CAAC;AAAA;AAAA;AACnD,wBAAU,KAAK,QAAQ;AACvB,6BAAe,KAAK,OAAO,KAAK,QAAQ,CAAC;AAGzC,kBAAI,SAAS;AACX,sBAAM,eAAe;AAAA,kBACnB,GAAG;AAAA,kBACH,SAAS,CAAC,EAAE,OAAO,OAAO,EAAE,QAAQ,GAAG,UAAU,MAAM,eAAe,KAAK,CAAC;AAAA,gBAC9E;AACA,sBAAM,cAAc,SAAS,KAAK,UAAU,YAAY,CAAC;AAAA;AAAA;AACzD,0BAAU,KAAK,WAAW;AAC1B,+BAAe,KAAK,OAAO,KAAK,WAAW,CAAC;AAAA,cAC9C;AAGA,oBAAM,YAAY,OAAO,SAAS,cAAc,OAAO,OAAO;AAC9D,kBAAI,aAAa,UAAU,SAAS,GAAG;AACrC,sBAAM,gBAAgB;AAAA,kBACpB,GAAG;AAAA,kBACH,SAAS;AAAA,oBACP;AAAA,sBACE;AAAA,sBACA,OAAO,EAAE,YAAY,UAAU;AAAA,sBAC/B,UAAU;AAAA,sBACV,eAAe;AAAA,oBACjB;AAAA,kBACF;AAAA,gBACF;AACA,sBAAM,eAAe,SAAS,KAAK,UAAU,aAAa,CAAC;AAAA;AAAA;AAC3D,0BAAU,KAAK,YAAY;AAC3B,+BAAe,KAAK,OAAO,KAAK,YAAY,CAAC;AAAA,cAC/C;AAGA,oBAAM,cAAc;AAAA,gBAClB,GAAG;AAAA,gBACH,SAAS;AAAA,kBACP;AAAA,oBACE;AAAA,oBACA,OAAO,CAAC;AAAA,oBACR,UAAU;AAAA,oBACV,eACE,aAAa,UAAU,SAAS,IAC5B,eACC,OAAO,iBAAiB;AAAA,kBACjC;AAAA,gBACF;AAAA,cACF;AACA,oBAAM,aAAa,SAAS,KAAK,UAAU,WAAW,CAAC;AAAA;AAAA;AACvD,wBAAU,KAAK,UAAU;AACzB,6BAAe,KAAK,OAAO,KAAK,UAAU,CAAC;AAAA,YAC7C;AAAA,UACF;AAAA,QACF,QAAQ;AAEN,gBAAM,UAAU,SAAS,OAAO;AAAA;AAAA;AAChC,oBAAU,KAAK,OAAO;AACtB,yBAAe,KAAK,OAAO,KAAK,OAAO,CAAC;AAAA,QAC1C;AAAA,MACF;AAGA,gBAAU,KAAK,kBAAkB;AACjC,qBAAe,KAAK,OAAO,KAAK,kBAAkB,CAAC;AACnD,UAAI,IAAI;AAGR,mBAAa,SAAS,UAAU;AAAA,QAC9B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,oBAAoB;AAAA,QAC/C,MAAM,OAAO,OAAO,cAAc;AAAA,QAClC,aAAa,KAAK,IAAI;AAAA,MACxB,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,kBAA0C,CAAC;AACjD,eAAS,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AAEvC,YAAI,QAAQ,uBAAuB,QAAQ,gBAAgB,QAAQ;AACjE;AACF,wBAAgB,GAAG,IAAI;AAAA,MACzB,CAAC;AAED,UAAI,UAAU,SAAS,QAAQ,eAAe;AAE9C,UAAI,SAAS,MAAM;AACjB,cAAM,SAAS,SAAS,KAAK,UAAU;AACvC,YAAI;AACF,iBAAO,MAAM;AACX,kBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,gBAAI,KAAM;AACV,kBAAM,QAAQ,OAAO,KAAK,KAAK;AAC/B,sBAAU,KAAK,KAAK;AACpB,2BAAe,KAAK,KAAK;AAAA,UAC3B;AAAA,QACF,UAAE;AACA,iBAAO,YAAY;AAAA,QACrB;AAAA,MACF;AAEA,UAAI,IAAI;AAGR,mBAAa,SAAS,UAAU;AAAA,QAC9B,QAAQ,SAAS;AAAA,QACjB,SAAS;AAAA,QACT,MAAM,OAAO,OAAO,cAAc;AAAA,QAClC,aAAa,KAAK,IAAI;AAAA,MACxB,CAAC;AAAA,IACH;AAGA,QAAI,wBAAwB,QAAW;AACrC,qBAAe,gBAAgB,mBAAmB;AAAA,IACpD;AAGA,gBAAY;AAAA,EACd,SAAS,KAAK;AAEZ,iBAAa,SAAS;AAGtB,QAAI,mBAAmB;AACrB,oBAAc,iBAAiB;AAC/B,0BAAoB;AAAA,IACtB;AAGA,iBAAa,eAAe,QAAQ;AAGpC,mBAAe,WAAW;AAG1B,QAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,YAAM,IAAI,MAAM,2BAA2B,SAAS,IAAI;AAAA,IAC1D;AAEA,UAAM;AAAA,EACR;AAKA,MAAI,iBAAiB;AAEnB,UAAM,uBAAuB,KAAK,KAAK,KAAK,SAAS,CAAC;AACtD,UAAM,gBAAgB;AAAA,MACpB,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAEA,UAAM,iBAAiB,cAAc,eAAe;AACpD,UAAM,qBAAqB,cAAc,eAAe;AACxD,UAAM,QAAoB;AAAA,MACxB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,OAAO,gBAAgB;AAAA,MACvB,MAAM,gBAAgB;AAAA,MACtB,MAAM;AAAA,MACN,cAAc;AAAA,MACd,SAAS,cAAc;AAAA,MACvB,WAAW,KAAK,IAAI,IAAI;AAAA,IAC1B;AACA,aAAS,KAAK,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAChC;AACF;;;Aev0DA,SAAS,WAAW,YAAAE,WAAU,SAAAC,cAAa;AAC3C,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AACxB,SAAS,oBAAoB,uBAAAC,4BAA2B;AAGxD,IAAM,aAAaF,MAAKC,SAAQ,GAAG,aAAa,UAAU;AAC1D,IAAM,cAAcD,MAAK,YAAY,YAAY;AAQjD,eAAe,kBAA+C;AAC5D,MAAI;AACF,UAAM,OAAO,MAAMG,UAAS,aAAa,OAAO,GAAG,KAAK;AACxD,QAAI,IAAI,WAAW,IAAI,KAAK,IAAI,WAAW,GAAI,QAAO;AAAA,EACxD,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAKA,eAAe,wBAAmE;AAChF,QAAM,MAAM,mBAAmB;AAC/B,QAAM,UAAUC,qBAAoB,GAAG;AACvC,QAAMC,OAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,UAAU,aAAa,MAAM,MAAM,EAAE,MAAM,IAAM,CAAC;AACxD,SAAO,EAAE,KAAK,SAAS,QAAQ,QAAQ;AACzC;AAMA,eAAsB,6BAInB;AAED,QAAM,QAAQ,MAAM,gBAAgB;AACpC,MAAI,OAAO;AACT,UAAM,UAAUD,qBAAoB,KAAsB;AAC1D,WAAO,EAAE,KAAK,OAAO,SAAS,QAAQ,SAAS,QAAQ,QAAQ;AAAA,EACjE;AAGA,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,IAAI,KAAK,OAAO,WAAW,IAAI;AACjF,UAAM,UAAUA,qBAAoB,MAAuB;AAC3D,WAAO,EAAE,KAAK,QAAQ,SAAS,QAAQ,SAAS,QAAQ,MAAM;AAAA,EAChE;AAGA,QAAM,EAAE,KAAK,QAAQ,IAAI,MAAM,sBAAsB;AACrD,SAAO,EAAE,KAAK,SAAS,QAAQ,YAAY;AAC7C;;;ACnEA,SAAS,YAAkB;AACzB,UAAQ,IAAI;AAAA,cACA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kDAQ6B,aAAa,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAiB/D;AACD;AAEA,SAAS,UAAU,MAAoE;AACrF,QAAM,SAAS,EAAE,SAAS,OAAO,MAAM,OAAO,MAAM,OAAgC;AAEpF,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,eAAe,QAAQ,MAAM;AACvC,aAAO,UAAU;AAAA,IACnB,WAAW,QAAQ,YAAY,QAAQ,MAAM;AAC3C,aAAO,OAAO;AAAA,IAChB,WAAW,QAAQ,YAAY,KAAK,IAAI,CAAC,GAAG;AAC1C,aAAO,OAAO,SAAS,KAAK,IAAI,CAAC,GAAG,EAAE;AACtC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,OAAsB;AACnC,QAAM,OAAO,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;AAE5C,MAAI,KAAK,SAAS;AAChB,YAAQ,IAAI,OAAO;AACnB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,KAAK,MAAM;AACb,cAAU;AACV,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,EAAE,KAAK,WAAW,SAAS,OAAO,IAAI,MAAM,2BAA2B;AAE7E,MAAI,WAAW,aAAa;AAC1B,YAAQ,IAAI,sCAAsC,OAAO,EAAE;AAAA,EAC7D,WAAW,WAAW,SAAS;AAC7B,YAAQ,IAAI,oCAAoC,OAAO,EAAE;AAAA,EAC3D,OAAO;AACL,YAAQ,IAAI,uDAAuD,OAAO,EAAE;AAAA,EAC9E;AAGA,QAAM,QAAQ,MAAM,WAAW;AAAA,IAC7B;AAAA,IACA,MAAM,KAAK;AAAA,IACX,SAAS,CAAC,SAAS;AACjB,cAAQ,IAAI,oDAAoD,IAAI,EAAE;AACtE,cAAQ,IAAI,+CAA+C,IAAI,SAAS;AAAA,IAC1E;AAAA,IACA,SAAS,CAAC,UAAU;AAClB,cAAQ,MAAM,uBAAuB,MAAM,OAAO,EAAE;AAAA,IACtD;AAAA,IACA,UAAU,CAAC,aAAa;AACtB,YAAM,OAAO,SAAS,aAAa,QAAQ,CAAC;AAC5C,YAAM,SAAS,SAAS,UAAU,KAAK,QAAQ,CAAC;AAChD,cAAQ,IAAI,iBAAiB,SAAS,IAAI,KAAK,SAAS,KAAK,KAAK,IAAI,WAAW,KAAK,IAAI;AAAA,IAC5F;AAAA,IACA,cAAc,CAAC,SAAS;AACtB,cAAQ,KAAK,6BAA6B,KAAK,UAAU,WAAW,KAAK,aAAa,EAAE;AAAA,IAC1F;AAAA,IACA,qBAAqB,CAAC,SAAS;AAC7B,cAAQ;AAAA,QACN,6CAA6C,KAAK,UAAU,WAAW,KAAK,WAAW;AAAA,MACzF;AAAA,IACF;AAAA,EACF,CAAC;AAGD,QAAM,UAAU,IAAI,eAAe,OAAO;AAC1C,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,aAAa;AAC3C,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,uDAAuD;AACnE,cAAQ,IAAI,gDAAgD,OAAO,EAAE;AAAA,IACvE,WAAW,QAAQ,OAAO;AACxB,cAAQ,IAAI,gCAAgC,QAAQ,UAAU,QAAQ;AAAA,IACxE,OAAO;AACL,cAAQ,IAAI,gCAAgC,QAAQ,UAAU,EAAE;AAAA,IAClE;AAAA,EACF,QAAQ;AACN,YAAQ,IAAI,wBAAwB,OAAO,0BAA0B;AAAA,EACvE;AAEA,UAAQ,IAAI,qCAAqC;AAGjD,QAAM,WAAW,OAAO,WAAmB;AACzC,YAAQ,IAAI;AAAA,wBAA2B,MAAM,oBAAoB;AACjE,QAAI;AACF,YAAM,MAAM,MAAM;AAClB,cAAQ,IAAI,2BAA2B;AACvC,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,KAAK;AACZ,cAAQ,MAAM,uCAAuC,GAAG,EAAE;AAC1D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,UAAQ,GAAG,UAAU,MAAM,SAAS,QAAQ,CAAC;AAC7C,UAAQ,GAAG,WAAW,MAAM,SAAS,SAAS,CAAC;AAG/C,QAAM,IAAI,QAAQ,MAAM;AAAA,EAAC,CAAC;AAC5B;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,6BAA6B,IAAI,OAAO,EAAE;AACxD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["privateKeyToAccount","response","paymentHeader","confidence","join","homedir","LOG_DIR","DEFAULT_TTL_MS","USDC_BASE","join","require","sanitized","account","privateKeyToAccount","balanceMonitor","baseUrl","existingWallet","readFile","mkdir","join","homedir","privateKeyToAccount","readFile","privateKeyToAccount","mkdir"]}
|
|
1
|
+
{"version":3,"sources":["../src/proxy.ts","../src/x402.ts","../src/payment-cache.ts","../src/router/rules.ts","../src/router/selector.ts","../src/router/config.ts","../src/router/index.ts","../src/models.ts","../src/logger.ts","../src/stats.ts","../src/dedup.ts","../src/balance.ts","../src/errors.ts","../src/version.ts","../src/session.ts","../src/auth.ts","../src/cli.ts"],"sourcesContent":["/**\n * Local x402 Proxy Server\n *\n * Sits between OpenClaw's pi-ai (which makes standard OpenAI-format requests)\n * and BlockRun's API (which requires x402 micropayments).\n *\n * Flow:\n * pi-ai → http://localhost:{port}/v1/chat/completions\n * → proxy forwards to https://blockrun.ai/api/v1/chat/completions\n * → gets 402 → @x402/fetch signs payment → retries\n * → streams response back to pi-ai\n *\n * Optimizations (v0.3.0):\n * - SSE heartbeat: for streaming requests, sends headers + heartbeat immediately\n * before the x402 flow, preventing OpenClaw's 10-15s timeout from firing.\n * - Response dedup: hashes request bodies and caches responses for 30s,\n * preventing double-charging when OpenClaw retries after timeout.\n * - Payment cache: after first 402, pre-signs subsequent requests to skip\n * the 402 round trip (~200ms savings per request).\n * - Smart routing: when model is \"blockrun/auto\", classify query and pick cheapest model.\n * - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { finished } from \"node:stream\";\nimport type { AddressInfo } from \"node:net\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { createPaymentFetch, type PreAuthParams } from \"./x402.js\";\nimport {\n route,\n getFallbackChain,\n getFallbackChainFiltered,\n calculateModelCost,\n DEFAULT_ROUTING_CONFIG,\n type RouterOptions,\n type RoutingDecision,\n type RoutingConfig,\n type ModelPricing,\n} from \"./router/index.js\";\nimport {\n BLOCKRUN_MODELS,\n resolveModelAlias,\n getModelContextWindow,\n isReasoningModel,\n} from \"./models.js\";\nimport { logUsage, type UsageEntry } from \"./logger.js\";\nimport { getStats } from \"./stats.js\";\nimport { RequestDeduplicator } from \"./dedup.js\";\nimport { BalanceMonitor } from \"./balance.js\";\n// Error classes available for programmatic use but not used in proxy\n// (universal free fallback means we don't throw balance errors anymore)\n// import { InsufficientFundsError, EmptyWalletError } from \"./errors.js\";\nimport { USER_AGENT } from \"./version.js\";\nimport { SessionStore, getSessionId, type SessionConfig } from \"./session.js\";\n\nconst BLOCKRUN_API = \"https://blockrun.ai/api\";\nconst AUTO_MODEL = \"blockrun/auto\";\nconst AUTO_MODEL_SHORT = \"auto\"; // OpenClaw strips provider prefix\nconst FREE_MODEL = \"nvidia/gpt-oss-120b\"; // Free model for empty wallet fallback\nconst HEARTBEAT_INTERVAL_MS = 2_000;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 180_000; // 3 minutes (allows for on-chain tx + LLM response)\nconst DEFAULT_PORT = 8402;\nconst MAX_FALLBACK_ATTEMPTS = 3; // Maximum models to try in fallback chain\nconst HEALTH_CHECK_TIMEOUT_MS = 2_000; // Timeout for checking existing proxy\nconst RATE_LIMIT_COOLDOWN_MS = 60_000; // 60 seconds cooldown for rate-limited models\nconst PORT_RETRY_ATTEMPTS = 5; // Max attempts to bind port (handles TIME_WAIT)\nconst PORT_RETRY_DELAY_MS = 1_000; // Delay between retry attempts\n\n/**\n * Transform upstream payment errors into user-friendly messages.\n * Parses the raw x402 error and formats it nicely.\n */\nfunction transformPaymentError(errorBody: string): string {\n try {\n // Try to parse the error JSON\n const parsed = JSON.parse(errorBody) as {\n error?: string;\n details?: string;\n };\n\n // Check if this is a payment verification error\n if (parsed.error === \"Payment verification failed\" && parsed.details) {\n // Extract the nested JSON from details\n // Format: \"Verification failed: {json}\\n\"\n const match = parsed.details.match(/Verification failed:\\s*(\\{.*\\})/s);\n if (match) {\n const innerJson = JSON.parse(match[1]) as {\n invalidMessage?: string;\n invalidReason?: string;\n payer?: string;\n };\n\n if (innerJson.invalidReason === \"insufficient_funds\" && innerJson.invalidMessage) {\n // Parse \"insufficient balance: 251 < 11463\"\n const balanceMatch = innerJson.invalidMessage.match(\n /insufficient balance:\\s*(\\d+)\\s*<\\s*(\\d+)/i,\n );\n if (balanceMatch) {\n const currentMicros = parseInt(balanceMatch[1], 10);\n const requiredMicros = parseInt(balanceMatch[2], 10);\n const currentUSD = (currentMicros / 1_000_000).toFixed(6);\n const requiredUSD = (requiredMicros / 1_000_000).toFixed(6);\n const wallet = innerJson.payer || \"unknown\";\n const shortWallet =\n wallet.length > 12 ? `${wallet.slice(0, 6)}...${wallet.slice(-4)}` : wallet;\n\n return JSON.stringify({\n error: {\n message: `Insufficient USDC balance. Current: $${currentUSD}, Required: ~$${requiredUSD}`,\n type: \"insufficient_funds\",\n wallet: wallet,\n current_balance_usd: currentUSD,\n required_usd: requiredUSD,\n help: `Fund wallet ${shortWallet} with USDC on Base, or use free model: /model free`,\n },\n });\n }\n }\n }\n }\n } catch {\n // If parsing fails, return original\n }\n return errorBody;\n}\n\n/**\n * Track rate-limited models to avoid hitting them again.\n * Maps model ID to the timestamp when the rate limit was hit.\n */\nconst rateLimitedModels = new Map<string, number>();\n\n/**\n * Check if a model is currently rate-limited (in cooldown period).\n */\nfunction isRateLimited(modelId: string): boolean {\n const hitTime = rateLimitedModels.get(modelId);\n if (!hitTime) return false;\n\n const elapsed = Date.now() - hitTime;\n if (elapsed >= RATE_LIMIT_COOLDOWN_MS) {\n rateLimitedModels.delete(modelId);\n return false;\n }\n return true;\n}\n\n/**\n * Mark a model as rate-limited.\n */\nfunction markRateLimited(modelId: string): void {\n rateLimitedModels.set(modelId, Date.now());\n console.log(`[ClawRouter] Model ${modelId} rate-limited, will deprioritize for 60s`);\n}\n\n/**\n * Reorder models to put rate-limited ones at the end.\n */\nfunction prioritizeNonRateLimited(models: string[]): string[] {\n const available: string[] = [];\n const rateLimited: string[] = [];\n\n for (const model of models) {\n if (isRateLimited(model)) {\n rateLimited.push(model);\n } else {\n available.push(model);\n }\n }\n\n return [...available, ...rateLimited];\n}\n\n/**\n * Check if response socket is writable (prevents write-after-close errors).\n * Returns true only if all conditions are safe for writing.\n */\nfunction canWrite(res: ServerResponse): boolean {\n return (\n !res.writableEnded &&\n !res.destroyed &&\n res.socket !== null &&\n !res.socket.destroyed &&\n res.socket.writable\n );\n}\n\n/**\n * Safe write with backpressure handling.\n * Returns true if write succeeded, false if socket is closed or write failed.\n */\nfunction safeWrite(res: ServerResponse, data: string | Buffer): boolean {\n if (!canWrite(res)) {\n return false;\n }\n return res.write(data);\n}\n\n// Extra buffer for balance check (on top of estimateAmount's 20% buffer)\n// Total effective buffer: 1.2 * 1.5 = 1.8x (80% safety margin)\n// This prevents x402 payment failures after streaming headers are sent,\n// which would trigger OpenClaw's 5-24 hour billing cooldown.\nconst BALANCE_CHECK_BUFFER = 1.5;\n\n/**\n * Get the proxy port from environment variable or default.\n */\nexport function getProxyPort(): number {\n const envPort = process.env.BLOCKRUN_PROXY_PORT;\n if (envPort) {\n const parsed = parseInt(envPort, 10);\n if (!isNaN(parsed) && parsed > 0 && parsed < 65536) {\n return parsed;\n }\n }\n return DEFAULT_PORT;\n}\n\n/**\n * Check if a proxy is already running on the given port.\n * Returns the wallet address if running, undefined otherwise.\n */\nasync function checkExistingProxy(port: number): Promise<string | undefined> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);\n\n try {\n const response = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n\n if (response.ok) {\n const data = (await response.json()) as { status?: string; wallet?: string };\n if (data.status === \"ok\" && data.wallet) {\n return data.wallet;\n }\n }\n return undefined;\n } catch {\n clearTimeout(timeoutId);\n return undefined;\n }\n}\n\n/**\n * Error patterns that indicate a provider-side issue (not user's fault).\n * These errors should trigger fallback to the next model in the chain.\n */\nconst PROVIDER_ERROR_PATTERNS = [\n /billing/i,\n /insufficient.*balance/i,\n /credits/i,\n /quota.*exceeded/i,\n /rate.*limit/i,\n /model.*unavailable/i,\n /model.*not.*available/i,\n /service.*unavailable/i,\n /capacity/i,\n /overloaded/i,\n /temporarily.*unavailable/i,\n /api.*key.*invalid/i,\n /authentication.*failed/i,\n];\n\n/**\n * HTTP status codes that indicate provider issues worth retrying with fallback.\n */\nconst FALLBACK_STATUS_CODES = [\n 400, // Bad request - sometimes used for billing errors\n 401, // Unauthorized - provider API key issues\n 402, // Payment required - but from upstream, not x402\n 403, // Forbidden - provider restrictions\n 429, // Rate limited\n 500, // Internal server error\n 502, // Bad gateway\n 503, // Service unavailable\n 504, // Gateway timeout\n];\n\n/**\n * Check if an error response indicates a provider issue that should trigger fallback.\n */\nfunction isProviderError(status: number, body: string): boolean {\n // Check status code first\n if (!FALLBACK_STATUS_CODES.includes(status)) {\n return false;\n }\n\n // For 5xx errors, always fallback\n if (status >= 500) {\n return true;\n }\n\n // For 4xx errors, check the body for known provider error patterns\n return PROVIDER_ERROR_PATTERNS.some((pattern) => pattern.test(body));\n}\n\n/**\n * Valid message roles for OpenAI-compatible APIs.\n * Some clients send non-standard roles (e.g., \"developer\" instead of \"system\").\n */\nconst VALID_ROLES = new Set([\"system\", \"user\", \"assistant\", \"tool\", \"function\"]);\n\n/**\n * Role mappings for non-standard roles.\n * Maps client-specific roles to standard OpenAI roles.\n */\nconst ROLE_MAPPINGS: Record<string, string> = {\n developer: \"system\", // OpenAI's newer API uses \"developer\" for system messages\n model: \"assistant\", // Some APIs use \"model\" instead of \"assistant\"\n};\n\ntype ChatMessage = { role: string; content: string | unknown };\n\n/**\n * Anthropic tool ID pattern: only alphanumeric, underscore, and hyphen allowed.\n * Error: \"messages.X.content.Y.tool_use.id: String should match pattern '^[a-zA-Z0-9_-]+$'\"\n */\nconst VALID_TOOL_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;\n\n/**\n * Sanitize a tool ID to match Anthropic's required pattern.\n * Replaces invalid characters with underscores.\n */\nfunction sanitizeToolId(id: string | undefined): string | undefined {\n if (!id || typeof id !== \"string\") return id;\n if (VALID_TOOL_ID_PATTERN.test(id)) return id;\n\n // Replace invalid characters with underscores\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\n/**\n * Type for messages with tool calls (OpenAI format).\n */\ntype MessageWithTools = ChatMessage & {\n tool_calls?: Array<{ id?: string; type?: string; function?: unknown }>;\n tool_call_id?: string;\n};\n\n/**\n * Type for content blocks that may contain tool IDs (Anthropic format in OpenAI wrapper).\n */\ntype ContentBlock = {\n type?: string;\n id?: string;\n tool_use_id?: string;\n [key: string]: unknown;\n};\n\n/**\n * Sanitize all tool IDs in messages to match Anthropic's pattern.\n * Handles both OpenAI format (tool_calls, tool_call_id) and content block formats.\n */\nfunction sanitizeToolIds(messages: ChatMessage[]): ChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n let hasChanges = false;\n const sanitized = messages.map((msg) => {\n const typedMsg = msg as MessageWithTools;\n let msgChanged = false;\n let newMsg = { ...msg } as MessageWithTools;\n\n // Sanitize tool_calls[].id in assistant messages\n if (typedMsg.tool_calls && Array.isArray(typedMsg.tool_calls)) {\n const newToolCalls = typedMsg.tool_calls.map((tc) => {\n if (tc.id && typeof tc.id === \"string\") {\n const sanitized = sanitizeToolId(tc.id);\n if (sanitized !== tc.id) {\n msgChanged = true;\n return { ...tc, id: sanitized };\n }\n }\n return tc;\n });\n if (msgChanged) {\n newMsg = { ...newMsg, tool_calls: newToolCalls };\n }\n }\n\n // Sanitize tool_call_id in tool messages\n if (typedMsg.tool_call_id && typeof typedMsg.tool_call_id === \"string\") {\n const sanitized = sanitizeToolId(typedMsg.tool_call_id);\n if (sanitized !== typedMsg.tool_call_id) {\n msgChanged = true;\n newMsg = { ...newMsg, tool_call_id: sanitized };\n }\n }\n\n // Sanitize content blocks if content is an array (Anthropic-style content)\n if (Array.isArray(typedMsg.content)) {\n const newContent = (typedMsg.content as ContentBlock[]).map((block) => {\n if (!block || typeof block !== \"object\") return block;\n\n let blockChanged = false;\n let newBlock = { ...block };\n\n // tool_use blocks have \"id\"\n if (block.type === \"tool_use\" && block.id && typeof block.id === \"string\") {\n const sanitized = sanitizeToolId(block.id);\n if (sanitized !== block.id) {\n blockChanged = true;\n newBlock = { ...newBlock, id: sanitized };\n }\n }\n\n // tool_result blocks have \"tool_use_id\"\n if (\n block.type === \"tool_result\" &&\n block.tool_use_id &&\n typeof block.tool_use_id === \"string\"\n ) {\n const sanitized = sanitizeToolId(block.tool_use_id);\n if (sanitized !== block.tool_use_id) {\n blockChanged = true;\n newBlock = { ...newBlock, tool_use_id: sanitized };\n }\n }\n\n if (blockChanged) {\n msgChanged = true;\n return newBlock;\n }\n return block;\n });\n\n if (msgChanged) {\n newMsg = { ...newMsg, content: newContent };\n }\n }\n\n if (msgChanged) {\n hasChanges = true;\n return newMsg;\n }\n return msg;\n });\n\n return hasChanges ? sanitized : messages;\n}\n\n/**\n * Normalize message roles to standard OpenAI format.\n * Converts non-standard roles (e.g., \"developer\") to valid ones.\n */\nfunction normalizeMessageRoles(messages: ChatMessage[]): ChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n let hasChanges = false;\n const normalized = messages.map((msg) => {\n if (VALID_ROLES.has(msg.role)) return msg;\n\n const mappedRole = ROLE_MAPPINGS[msg.role];\n if (mappedRole) {\n hasChanges = true;\n return { ...msg, role: mappedRole };\n }\n\n // Unknown role - default to \"user\" to avoid API errors\n hasChanges = true;\n return { ...msg, role: \"user\" };\n });\n\n return hasChanges ? normalized : messages;\n}\n\n/**\n * Normalize messages for Google models.\n * Google's Gemini API requires the first non-system message to be from \"user\".\n * If conversation starts with \"assistant\"/\"model\", prepend a placeholder user message.\n */\n\nfunction normalizeMessagesForGoogle(messages: ChatMessage[]): ChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n // Find first non-system message\n let firstNonSystemIdx = -1;\n for (let i = 0; i < messages.length; i++) {\n if (messages[i].role !== \"system\") {\n firstNonSystemIdx = i;\n break;\n }\n }\n\n // If no non-system messages, return as-is\n if (firstNonSystemIdx === -1) return messages;\n\n const firstRole = messages[firstNonSystemIdx].role;\n\n // If first non-system message is already \"user\", no change needed\n if (firstRole === \"user\") return messages;\n\n // If first non-system message is \"assistant\" or \"model\", prepend a user message\n if (firstRole === \"assistant\" || firstRole === \"model\") {\n const normalized = [...messages];\n normalized.splice(firstNonSystemIdx, 0, {\n role: \"user\",\n content: \"(continuing conversation)\",\n });\n return normalized;\n }\n\n return messages;\n}\n\n/**\n * Check if a model is a Google model that requires message normalization.\n */\nfunction isGoogleModel(modelId: string): boolean {\n return modelId.startsWith(\"google/\") || modelId.startsWith(\"gemini\");\n}\n\n/**\n * Extended message type for thinking-enabled conversations.\n */\ntype ExtendedChatMessage = ChatMessage & {\n tool_calls?: unknown[];\n reasoning_content?: unknown;\n};\n\n/**\n * Normalize messages for thinking-enabled requests.\n * When thinking/extended_thinking is enabled, assistant messages with tool_calls\n * must have reasoning_content (can be empty string if not present).\n * Error: \"400 thinking is enabled but reasoning_content is missing in assistant tool call message\"\n */\nfunction normalizeMessagesForThinking(messages: ExtendedChatMessage[]): ExtendedChatMessage[] {\n if (!messages || messages.length === 0) return messages;\n\n let hasChanges = false;\n const normalized = messages.map((msg) => {\n // Skip if not assistant or already has reasoning_content\n if (msg.role !== \"assistant\" || msg.reasoning_content !== undefined) {\n return msg;\n }\n\n // Check for OpenAI format: tool_calls array\n const hasOpenAIToolCalls =\n msg.tool_calls && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;\n\n // Check for Anthropic format: content array with tool_use blocks\n const hasAnthropicToolUse =\n Array.isArray(msg.content) &&\n (msg.content as Array<{ type?: string }>).some((block) => block?.type === \"tool_use\");\n\n if (hasOpenAIToolCalls || hasAnthropicToolUse) {\n hasChanges = true;\n return { ...msg, reasoning_content: \"\" };\n }\n return msg;\n });\n\n return hasChanges ? normalized : messages;\n}\n\n// Kimi/Moonshot models use special Unicode tokens for thinking boundaries.\n// Pattern: <|begin▁of▁thinking|>content<|end▁of▁thinking|>\n// The | is fullwidth vertical bar (U+FF5C), ▁ is lower one-eighth block (U+2581).\n\n// Match full Kimi thinking blocks: <|begin...|>content<|end...|>\nconst KIMI_BLOCK_RE = /<[||][^<>]*begin[^<>]*[||]>[\\s\\S]*?<[||][^<>]*end[^<>]*[||]>/gi;\n\n// Match standalone Kimi tokens like <|end▁of▁thinking|>\nconst KIMI_TOKEN_RE = /<[||][^<>]*[||]>/g;\n\n// Standard thinking tags that may leak through from various models\nconst THINKING_TAG_RE = /<\\s*\\/?\\s*(?:think(?:ing)?|thought|antthinking)\\b[^>]*>/gi;\n\n// Full thinking blocks: <think>content</think>\nconst THINKING_BLOCK_RE =\n /<\\s*(?:think(?:ing)?|thought|antthinking)\\b[^>]*>[\\s\\S]*?<\\s*\\/\\s*(?:think(?:ing)?|thought|antthinking)\\s*>/gi;\n\n/**\n * Strip thinking tokens and blocks from model response content.\n * Handles both Kimi-style Unicode tokens and standard XML-style tags.\n *\n * NOTE: DSML tags (<|DSML|...>) are NOT stripped - those are tool calls\n * that should be handled by the API, not hidden from users.\n */\nfunction stripThinkingTokens(content: string): string {\n if (!content) return content;\n // Strip full Kimi thinking blocks first (begin...end with content)\n let cleaned = content.replace(KIMI_BLOCK_RE, \"\");\n // Strip remaining standalone Kimi tokens\n cleaned = cleaned.replace(KIMI_TOKEN_RE, \"\");\n // Strip full thinking blocks (<think>...</think>)\n cleaned = cleaned.replace(THINKING_BLOCK_RE, \"\");\n // Strip remaining standalone thinking tags\n cleaned = cleaned.replace(THINKING_TAG_RE, \"\");\n return cleaned;\n}\n\n/** Callback info for low balance warning */\nexport type LowBalanceInfo = {\n balanceUSD: string;\n walletAddress: string;\n};\n\n/** Callback info for insufficient funds error */\nexport type InsufficientFundsInfo = {\n balanceUSD: string;\n requiredUSD: string;\n walletAddress: string;\n};\n\nexport type ProxyOptions = {\n walletKey: string;\n apiBase?: string;\n /** Port to listen on (default: 8402) */\n port?: number;\n routingConfig?: Partial<RoutingConfig>;\n /** Request timeout in ms (default: 180000 = 3 minutes). Covers on-chain tx + LLM response. */\n requestTimeoutMs?: number;\n /** Skip balance checks (for testing only). Default: false */\n skipBalanceCheck?: boolean;\n /**\n * Session persistence config. When enabled, maintains model selection\n * across requests within a session to prevent mid-task model switching.\n */\n sessionConfig?: Partial<SessionConfig>;\n onReady?: (port: number) => void;\n onError?: (error: Error) => void;\n onPayment?: (info: { model: string; amount: string; network: string }) => void;\n onRouted?: (decision: RoutingDecision) => void;\n /** Called when balance drops below $1.00 (warning, request still proceeds) */\n onLowBalance?: (info: LowBalanceInfo) => void;\n /** Called when balance is insufficient for a request (request fails) */\n onInsufficientFunds?: (info: InsufficientFundsInfo) => void;\n};\n\nexport type ProxyHandle = {\n port: number;\n baseUrl: string;\n walletAddress: string;\n balanceMonitor: BalanceMonitor;\n close: () => Promise<void>;\n};\n\n/**\n * Build model pricing map from BLOCKRUN_MODELS.\n */\nfunction buildModelPricing(): Map<string, ModelPricing> {\n const map = new Map<string, ModelPricing>();\n for (const m of BLOCKRUN_MODELS) {\n if (m.id === AUTO_MODEL) continue; // skip meta-model\n map.set(m.id, { inputPrice: m.inputPrice, outputPrice: m.outputPrice });\n }\n return map;\n}\n\n/**\n * Merge partial routing config overrides with defaults.\n */\nfunction mergeRoutingConfig(overrides?: Partial<RoutingConfig>): RoutingConfig {\n if (!overrides) return DEFAULT_ROUTING_CONFIG;\n return {\n ...DEFAULT_ROUTING_CONFIG,\n ...overrides,\n classifier: { ...DEFAULT_ROUTING_CONFIG.classifier, ...overrides.classifier },\n scoring: { ...DEFAULT_ROUTING_CONFIG.scoring, ...overrides.scoring },\n tiers: { ...DEFAULT_ROUTING_CONFIG.tiers, ...overrides.tiers },\n overrides: { ...DEFAULT_ROUTING_CONFIG.overrides, ...overrides.overrides },\n };\n}\n\n/**\n * Estimate USDC cost for a request based on model pricing.\n * Returns amount string in USDC smallest unit (6 decimals) or undefined if unknown.\n */\nfunction estimateAmount(\n modelId: string,\n bodyLength: number,\n maxTokens: number,\n): string | undefined {\n const model = BLOCKRUN_MODELS.find((m) => m.id === modelId);\n if (!model) return undefined;\n\n // Rough estimate: ~4 chars per token for input\n const estimatedInputTokens = Math.ceil(bodyLength / 4);\n const estimatedOutputTokens = maxTokens || model.maxOutput || 4096;\n\n const costUsd =\n (estimatedInputTokens / 1_000_000) * model.inputPrice +\n (estimatedOutputTokens / 1_000_000) * model.outputPrice;\n\n // Convert to USDC 6-decimal integer, add 20% buffer for estimation error\n // Minimum 100 ($0.0001) to avoid zero-amount rejections\n const amountMicros = Math.max(100, Math.ceil(costUsd * 1.2 * 1_000_000));\n return amountMicros.toString();\n}\n\n/**\n * Start the local x402 proxy server.\n *\n * If a proxy is already running on the target port, reuses it instead of failing.\n * Port can be configured via BLOCKRUN_PROXY_PORT environment variable.\n *\n * Returns a handle with the assigned port, base URL, and a close function.\n */\nexport async function startProxy(options: ProxyOptions): Promise<ProxyHandle> {\n const apiBase = options.apiBase ?? BLOCKRUN_API;\n\n // Determine port: options.port > env var > default\n const listenPort = options.port ?? getProxyPort();\n\n // Check if a proxy is already running on this port\n const existingWallet = await checkExistingProxy(listenPort);\n if (existingWallet) {\n // Proxy already running — reuse it instead of failing with EADDRINUSE\n const account = privateKeyToAccount(options.walletKey as `0x${string}`);\n const balanceMonitor = new BalanceMonitor(account.address);\n const baseUrl = `http://127.0.0.1:${listenPort}`;\n\n // Verify the existing proxy is using the same wallet (or warn if different)\n if (existingWallet !== account.address) {\n console.warn(\n `[ClawRouter] Existing proxy on port ${listenPort} uses wallet ${existingWallet}, but current config uses ${account.address}. Reusing existing proxy.`,\n );\n }\n\n options.onReady?.(listenPort);\n\n return {\n port: listenPort,\n baseUrl,\n walletAddress: existingWallet,\n balanceMonitor,\n close: async () => {\n // No-op: we didn't start this proxy, so we shouldn't close it\n },\n };\n }\n\n // Create x402 payment-enabled fetch from wallet private key\n const account = privateKeyToAccount(options.walletKey as `0x${string}`);\n const { fetch: payFetch } = createPaymentFetch(options.walletKey as `0x${string}`);\n\n // Create balance monitor for pre-request checks\n const balanceMonitor = new BalanceMonitor(account.address);\n\n // Build router options (100% local — no external API calls for routing)\n const routingConfig = mergeRoutingConfig(options.routingConfig);\n const modelPricing = buildModelPricing();\n const routerOpts: RouterOptions = {\n config: routingConfig,\n modelPricing,\n };\n\n // Request deduplicator (shared across all requests)\n const deduplicator = new RequestDeduplicator();\n\n // Session store for model persistence (prevents mid-task model switching)\n const sessionStore = new SessionStore(options.sessionConfig);\n\n // Track active connections for graceful cleanup\n const connections = new Set<import(\"net\").Socket>();\n\n const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Add stream error handlers to prevent server crashes\n req.on(\"error\", (err) => {\n console.error(`[ClawRouter] Request stream error: ${err.message}`);\n // Don't throw - just log and let request handler deal with it\n });\n\n res.on(\"error\", (err) => {\n console.error(`[ClawRouter] Response stream error: ${err.message}`);\n // Don't try to write to failed socket - just log\n });\n\n // Finished wrapper for guaranteed cleanup on response completion/error\n finished(res, (err) => {\n if (err && err.code !== \"ERR_STREAM_DESTROYED\") {\n console.error(`[ClawRouter] Response finished with error: ${err.message}`);\n }\n // Note: heartbeatInterval cleanup happens in res.on(\"close\") handler\n // Note: completed and dedup cleanup happens in the res.on(\"close\") handler below\n });\n\n // Request finished wrapper for complete stream lifecycle tracking\n finished(req, (err) => {\n if (err && err.code !== \"ERR_STREAM_DESTROYED\") {\n console.error(`[ClawRouter] Request finished with error: ${err.message}`);\n }\n });\n\n // Health check with optional balance info\n if (req.url === \"/health\" || req.url?.startsWith(\"/health?\")) {\n const url = new URL(req.url, \"http://localhost\");\n const full = url.searchParams.get(\"full\") === \"true\";\n\n const response: Record<string, unknown> = {\n status: \"ok\",\n wallet: account.address,\n };\n\n if (full) {\n try {\n const balanceInfo = await balanceMonitor.checkBalance();\n response.balance = balanceInfo.balanceUSD;\n response.isLow = balanceInfo.isLow;\n response.isEmpty = balanceInfo.isEmpty;\n } catch {\n response.balanceError = \"Could not fetch balance\";\n }\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(response));\n return;\n }\n\n // Stats API endpoint - returns JSON for programmatic access\n if (req.url === \"/stats\" || req.url?.startsWith(\"/stats?\")) {\n try {\n const url = new URL(req.url, \"http://localhost\");\n const days = parseInt(url.searchParams.get(\"days\") || \"7\", 10);\n const stats = await getStats(Math.min(days, 30));\n\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"Cache-Control\": \"no-cache\",\n });\n res.end(JSON.stringify(stats, null, 2));\n } catch (err) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: `Failed to get stats: ${err instanceof Error ? err.message : String(err)}`,\n }),\n );\n }\n return;\n }\n\n // --- Handle /v1/models locally (no upstream call needed) ---\n if (req.url === \"/v1/models\" && req.method === \"GET\") {\n const models = BLOCKRUN_MODELS.filter((m) => m.id !== \"blockrun/auto\").map((m) => ({\n id: m.id,\n object: \"model\",\n created: Math.floor(Date.now() / 1000),\n owned_by: m.id.split(\"/\")[0] || \"unknown\",\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ object: \"list\", data: models }));\n return;\n }\n\n // Only proxy paths starting with /v1\n if (!req.url?.startsWith(\"/v1\")) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n try {\n await proxyRequest(\n req,\n res,\n apiBase,\n payFetch,\n options,\n routerOpts,\n deduplicator,\n balanceMonitor,\n sessionStore,\n );\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n options.onError?.(error);\n\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: { message: `Proxy error: ${error.message}`, type: \"proxy_error\" },\n }),\n );\n } else if (!res.writableEnded) {\n // Headers already sent (streaming) — send error as SSE event\n res.write(\n `data: ${JSON.stringify({ error: { message: error.message, type: \"proxy_error\" } })}\\n\\n`,\n );\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n }\n }\n });\n\n // Listen on configured port with retry logic for TIME_WAIT handling\n // When gateway restarts quickly, the port may still be in TIME_WAIT state.\n // We retry with delay instead of incorrectly assuming a proxy is running.\n const tryListen = (attempt: number): Promise<void> => {\n return new Promise<void>((resolveAttempt, rejectAttempt) => {\n const onError = async (err: NodeJS.ErrnoException) => {\n server.removeListener(\"error\", onError);\n\n if (err.code === \"EADDRINUSE\") {\n // Port is in use - check if a proxy is actually running\n const existingWallet = await checkExistingProxy(listenPort);\n if (existingWallet) {\n // Proxy is actually running - this is fine, reuse it\n console.log(`[ClawRouter] Existing proxy detected on port ${listenPort}, reusing`);\n rejectAttempt({ code: \"REUSE_EXISTING\", wallet: existingWallet });\n return;\n }\n\n // Port is in TIME_WAIT (no proxy responding) - retry after delay\n if (attempt < PORT_RETRY_ATTEMPTS) {\n console.log(\n `[ClawRouter] Port ${listenPort} in TIME_WAIT, retrying in ${PORT_RETRY_DELAY_MS}ms (attempt ${attempt}/${PORT_RETRY_ATTEMPTS})`,\n );\n rejectAttempt({ code: \"RETRY\", attempt });\n return;\n }\n\n // Max retries exceeded\n console.error(\n `[ClawRouter] Port ${listenPort} still in use after ${PORT_RETRY_ATTEMPTS} attempts`,\n );\n rejectAttempt(err);\n return;\n }\n\n rejectAttempt(err);\n };\n\n server.once(\"error\", onError);\n server.listen(listenPort, \"127.0.0.1\", () => {\n server.removeListener(\"error\", onError);\n resolveAttempt();\n });\n });\n };\n\n // Retry loop for port binding\n let lastError: Error | undefined;\n for (let attempt = 1; attempt <= PORT_RETRY_ATTEMPTS; attempt++) {\n try {\n await tryListen(attempt);\n break; // Success\n } catch (err: unknown) {\n const error = err as { code?: string; wallet?: string; attempt?: number };\n\n if (error.code === \"REUSE_EXISTING\" && error.wallet) {\n // Proxy is running, reuse it\n const baseUrl = `http://127.0.0.1:${listenPort}`;\n options.onReady?.(listenPort);\n return {\n port: listenPort,\n baseUrl,\n walletAddress: error.wallet,\n balanceMonitor,\n close: async () => {\n // No-op: we didn't start this proxy, so we shouldn't close it\n },\n };\n }\n\n if (error.code === \"RETRY\") {\n // Wait before retry\n await new Promise((r) => setTimeout(r, PORT_RETRY_DELAY_MS));\n continue;\n }\n\n // Other error - throw\n lastError = err as Error;\n break;\n }\n }\n\n if (lastError) {\n throw lastError;\n }\n\n // Server is now listening - set up remaining handlers\n const addr = server.address() as AddressInfo;\n const port = addr.port;\n const baseUrl = `http://127.0.0.1:${port}`;\n\n options.onReady?.(port);\n\n // Add runtime error handler AFTER successful listen\n // This handles errors that occur during server operation (not just startup)\n server.on(\"error\", (err) => {\n console.error(`[ClawRouter] Server runtime error: ${err.message}`);\n options.onError?.(err);\n // Don't crash - log and continue\n });\n\n // Handle client connection errors (bad requests, socket errors)\n server.on(\"clientError\", (err, socket) => {\n console.error(`[ClawRouter] Client error: ${err.message}`);\n // Send 400 Bad Request if socket is still writable\n if (socket.writable && !socket.destroyed) {\n socket.end(\"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n }\n });\n\n // Track connections for graceful cleanup\n server.on(\"connection\", (socket) => {\n connections.add(socket);\n\n // Set 5-minute timeout for streaming requests\n socket.setTimeout(300_000);\n\n socket.on(\"timeout\", () => {\n console.error(`[ClawRouter] Socket timeout, destroying connection`);\n socket.destroy();\n });\n\n socket.on(\"end\", () => {\n // Half-closed by client (FIN received)\n });\n\n socket.on(\"error\", (err) => {\n console.error(`[ClawRouter] Socket error: ${err.message}`);\n });\n\n socket.on(\"close\", () => {\n connections.delete(socket);\n });\n });\n\n return {\n port,\n baseUrl,\n walletAddress: account.address,\n balanceMonitor,\n close: () =>\n new Promise<void>((res, rej) => {\n const timeout = setTimeout(() => {\n rej(new Error(\"[ClawRouter] Close timeout after 4s\"));\n }, 4000);\n\n sessionStore.close();\n // Destroy all active connections before closing server\n for (const socket of connections) {\n socket.destroy();\n }\n connections.clear();\n server.close((err) => {\n clearTimeout(timeout);\n if (err) {\n rej(err);\n } else {\n res();\n }\n });\n }),\n };\n}\n\n/** Result of attempting a model request */\ntype ModelRequestResult = {\n success: boolean;\n response?: Response;\n errorBody?: string;\n errorStatus?: number;\n isProviderError?: boolean;\n};\n\n/**\n * Attempt a request with a specific model.\n * Returns the response or error details for fallback decision.\n */\nasync function tryModelRequest(\n upstreamUrl: string,\n method: string,\n headers: Record<string, string>,\n body: Buffer,\n modelId: string,\n maxTokens: number,\n payFetch: (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ) => Promise<Response>,\n balanceMonitor: BalanceMonitor,\n signal: AbortSignal,\n): Promise<ModelRequestResult> {\n // Update model in body and normalize messages\n let requestBody = body;\n try {\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n parsed.model = modelId;\n\n // Normalize message roles (e.g., \"developer\" -> \"system\")\n if (Array.isArray(parsed.messages)) {\n parsed.messages = normalizeMessageRoles(parsed.messages as ChatMessage[]);\n }\n\n // Sanitize tool IDs to match Anthropic's pattern (alphanumeric, underscore, hyphen only)\n if (Array.isArray(parsed.messages)) {\n parsed.messages = sanitizeToolIds(parsed.messages as ChatMessage[]);\n }\n\n // Normalize messages for Google models (first non-system message must be \"user\")\n if (isGoogleModel(modelId) && Array.isArray(parsed.messages)) {\n parsed.messages = normalizeMessagesForGoogle(parsed.messages as ChatMessage[]);\n }\n\n // Normalize messages for thinking-enabled requests (add reasoning_content to tool calls)\n // Check request flags AND target model - reasoning models have thinking enabled server-side\n const hasThinkingEnabled = !!(\n parsed.thinking ||\n parsed.extended_thinking ||\n isReasoningModel(modelId)\n );\n if (hasThinkingEnabled && Array.isArray(parsed.messages)) {\n parsed.messages = normalizeMessagesForThinking(parsed.messages as ExtendedChatMessage[]);\n }\n\n requestBody = Buffer.from(JSON.stringify(parsed));\n } catch {\n // If body isn't valid JSON, use as-is\n }\n\n // Estimate cost for pre-auth\n const estimated = estimateAmount(modelId, requestBody.length, maxTokens);\n const preAuth: PreAuthParams | undefined = estimated ? { estimatedAmount: estimated } : undefined;\n\n try {\n const response = await payFetch(\n upstreamUrl,\n {\n method,\n headers,\n body: requestBody.length > 0 ? new Uint8Array(requestBody) : undefined,\n signal,\n },\n preAuth,\n );\n\n // Check for provider errors\n if (response.status !== 200) {\n // Clone response to read body without consuming it\n const errorBody = await response.text();\n const isProviderErr = isProviderError(response.status, errorBody);\n\n return {\n success: false,\n errorBody,\n errorStatus: response.status,\n isProviderError: isProviderErr,\n };\n }\n\n return { success: true, response };\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n return {\n success: false,\n errorBody: errorMsg,\n errorStatus: 500,\n isProviderError: true, // Network errors are retryable\n };\n }\n}\n\n/**\n * Proxy a single request through x402 payment flow to BlockRun API.\n *\n * Optimizations applied in order:\n * 1. Dedup check — if same request body seen within 30s, replay cached response\n * 2. Streaming heartbeat — for stream:true, send 200 + heartbeats immediately\n * 3. Payment pre-auth — estimate USDC amount and pre-sign to skip 402 round trip\n * 4. Smart routing — when model is \"blockrun/auto\", pick cheapest capable model\n * 5. Fallback chain — on provider errors, try next model in tier's fallback list\n */\nasync function proxyRequest(\n req: IncomingMessage,\n res: ServerResponse,\n apiBase: string,\n payFetch: (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ) => Promise<Response>,\n options: ProxyOptions,\n routerOpts: RouterOptions,\n deduplicator: RequestDeduplicator,\n balanceMonitor: BalanceMonitor,\n sessionStore: SessionStore,\n): Promise<void> {\n const startTime = Date.now();\n\n // Build upstream URL: /v1/chat/completions → https://blockrun.ai/api/v1/chat/completions\n const upstreamUrl = `${apiBase}${req.url}`;\n\n // Collect request body\n const bodyChunks: Buffer[] = [];\n for await (const chunk of req) {\n bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n let body = Buffer.concat(bodyChunks);\n\n // --- Smart routing ---\n let routingDecision: RoutingDecision | undefined;\n let isStreaming = false;\n let modelId = \"\";\n let maxTokens = 4096;\n const isChatCompletion = req.url?.includes(\"/chat/completions\");\n\n if (isChatCompletion && body.length > 0) {\n try {\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n isStreaming = parsed.stream === true;\n modelId = (parsed.model as string) || \"\";\n maxTokens = (parsed.max_tokens as number) || 4096;\n\n // Force stream: false — BlockRun API doesn't support streaming yet\n // ClawRouter handles SSE heartbeat simulation for upstream compatibility\n let bodyModified = false;\n if (parsed.stream === true) {\n parsed.stream = false;\n bodyModified = true;\n }\n\n // Normalize model name for comparison (trim whitespace, lowercase)\n const normalizedModel =\n typeof parsed.model === \"string\" ? parsed.model.trim().toLowerCase() : \"\";\n\n // Resolve model aliases (e.g., \"claude\" -> \"anthropic/claude-sonnet-4\")\n const resolvedModel = resolveModelAlias(normalizedModel);\n const wasAlias = resolvedModel !== normalizedModel;\n\n const isAutoModel =\n normalizedModel === AUTO_MODEL.toLowerCase() ||\n normalizedModel === AUTO_MODEL_SHORT.toLowerCase();\n\n // Debug: log received model name\n console.log(\n `[ClawRouter] Received model: \"${parsed.model}\" -> normalized: \"${normalizedModel}\"${wasAlias ? ` -> alias: \"${resolvedModel}\"` : \"\"}, isAuto: ${isAutoModel}`,\n );\n\n // If alias was resolved, update the model in the request\n if (wasAlias && !isAutoModel) {\n parsed.model = resolvedModel;\n modelId = resolvedModel;\n bodyModified = true;\n }\n\n if (isAutoModel) {\n // Check for session persistence - use pinned model if available\n const sessionId = getSessionId(\n req.headers as Record<string, string | string[] | undefined>,\n );\n const existingSession = sessionId ? sessionStore.getSession(sessionId) : undefined;\n\n if (existingSession) {\n // Use the session's pinned model instead of re-routing\n console.log(\n `[ClawRouter] Session ${sessionId?.slice(0, 8)}... using pinned model: ${existingSession.model}`,\n );\n parsed.model = existingSession.model;\n modelId = existingSession.model;\n bodyModified = true;\n sessionStore.touchSession(sessionId!);\n } else {\n // No session or expired - route normally\n // Extract prompt from messages\n type ChatMessage = { role: string; content: string };\n const messages = parsed.messages as ChatMessage[] | undefined;\n let lastUserMsg: ChatMessage | undefined;\n if (messages) {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"user\") {\n lastUserMsg = messages[i];\n break;\n }\n }\n }\n const systemMsg = messages?.find((m: ChatMessage) => m.role === \"system\");\n const prompt = typeof lastUserMsg?.content === \"string\" ? lastUserMsg.content : \"\";\n const systemPrompt =\n typeof systemMsg?.content === \"string\" ? systemMsg.content : undefined;\n\n // Tool detection no longer forces agentic mode\n // Agentic mode is now triggered by keyword-based detection (agenticScore >= 0.6)\n // This allows simple queries with tools to use cheaper models\n const tools = parsed.tools as unknown[] | undefined;\n const hasTools = Array.isArray(tools) && tools.length > 0;\n\n if (hasTools) {\n console.log(`[ClawRouter] Tools detected (${tools.length}), agentic mode via keywords`);\n }\n\n routingDecision = route(prompt, systemPrompt, maxTokens, routerOpts);\n\n // Replace model in body\n parsed.model = routingDecision.model;\n modelId = routingDecision.model;\n bodyModified = true;\n\n // Pin this model to the session for future requests\n if (sessionId) {\n sessionStore.setSession(sessionId, routingDecision.model, routingDecision.tier);\n console.log(\n `[ClawRouter] Session ${sessionId.slice(0, 8)}... pinned to model: ${routingDecision.model}`,\n );\n }\n\n options.onRouted?.(routingDecision);\n }\n }\n\n // Rebuild body if modified\n if (bodyModified) {\n body = Buffer.from(JSON.stringify(parsed));\n }\n } catch (err) {\n // Log routing errors so they're not silently swallowed\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(`[ClawRouter] Routing error: ${errorMsg}`);\n options.onError?.(new Error(`Routing failed: ${errorMsg}`));\n }\n }\n\n // --- Dedup check ---\n const dedupKey = RequestDeduplicator.hash(body);\n\n // Check completed cache first\n const cached = deduplicator.getCached(dedupKey);\n if (cached) {\n res.writeHead(cached.status, cached.headers);\n res.end(cached.body);\n return;\n }\n\n // Check in-flight — wait for the original request to complete\n const inflight = deduplicator.getInflight(dedupKey);\n if (inflight) {\n const result = await inflight;\n res.writeHead(result.status, result.headers);\n res.end(result.body);\n return;\n }\n\n // Register this request as in-flight\n deduplicator.markInflight(dedupKey);\n\n // --- Pre-request balance check ---\n // Estimate cost and check if wallet has sufficient balance\n // Skip if skipBalanceCheck is set (for testing) or if using free model\n let estimatedCostMicros: bigint | undefined;\n const isFreeModel = modelId === FREE_MODEL;\n\n if (modelId && !options.skipBalanceCheck && !isFreeModel) {\n const estimated = estimateAmount(modelId, body.length, maxTokens);\n if (estimated) {\n estimatedCostMicros = BigInt(estimated);\n\n // Apply extra buffer for balance check to prevent x402 failures after streaming starts.\n // This is aggressive to avoid triggering OpenClaw's 5-24 hour billing cooldown.\n const bufferedCostMicros =\n (estimatedCostMicros * BigInt(Math.ceil(BALANCE_CHECK_BUFFER * 100))) / 100n;\n\n // Check balance before proceeding (using buffered amount)\n const sufficiency = await balanceMonitor.checkSufficient(bufferedCostMicros);\n\n if (sufficiency.info.isEmpty || !sufficiency.sufficient) {\n // Wallet is empty or insufficient — ALWAYS fallback to free model\n // This ensures new users with empty wallets can still use ClawRouter\n const originalModel = modelId;\n console.log(\n `[ClawRouter] Wallet ${sufficiency.info.isEmpty ? \"empty\" : \"insufficient\"} ($${sufficiency.info.balanceUSD}), falling back to free model: ${FREE_MODEL} (requested: ${originalModel})`,\n );\n modelId = FREE_MODEL;\n // Update the body with new model\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n parsed.model = FREE_MODEL;\n body = Buffer.from(JSON.stringify(parsed));\n\n // Notify about the fallback\n options.onLowBalance?.({\n balanceUSD: sufficiency.info.balanceUSD,\n walletAddress: sufficiency.info.walletAddress,\n });\n } else if (sufficiency.info.isLow) {\n // Balance is low but sufficient — warn and proceed\n options.onLowBalance?.({\n balanceUSD: sufficiency.info.balanceUSD,\n walletAddress: sufficiency.info.walletAddress,\n });\n }\n }\n }\n\n // --- Streaming: early header flush + heartbeat ---\n let heartbeatInterval: ReturnType<typeof setInterval> | undefined;\n let headersSentEarly = false;\n\n if (isStreaming) {\n // Send 200 + SSE headers immediately, before x402 flow\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n });\n headersSentEarly = true;\n\n // First heartbeat immediately\n safeWrite(res, \": heartbeat\\n\\n\");\n\n // Continue heartbeats every 2s while waiting for upstream\n heartbeatInterval = setInterval(() => {\n if (canWrite(res)) {\n safeWrite(res, \": heartbeat\\n\\n\");\n } else {\n // Socket closed, stop heartbeat\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n\n // Forward headers, stripping host, connection, and content-length\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (\n key === \"host\" ||\n key === \"connection\" ||\n key === \"transfer-encoding\" ||\n key === \"content-length\"\n )\n continue;\n if (typeof value === \"string\") {\n headers[key] = value;\n }\n }\n if (!headers[\"content-type\"]) {\n headers[\"content-type\"] = \"application/json\";\n }\n headers[\"user-agent\"] = USER_AGENT;\n\n // --- Client disconnect cleanup ---\n let completed = false;\n res.on(\"close\", () => {\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n // Remove from in-flight if client disconnected before completion\n if (!completed) {\n deduplicator.removeInflight(dedupKey);\n }\n });\n\n // --- Request timeout ---\n const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n // --- Build fallback chain ---\n // If we have a routing decision, get the full fallback chain for the tier\n // Otherwise, just use the current model (no fallback for explicit model requests)\n let modelsToTry: string[];\n if (routingDecision) {\n // Estimate total context: input tokens (~4 chars per token) + max output tokens\n const estimatedInputTokens = Math.ceil(body.length / 4);\n const estimatedTotalTokens = estimatedInputTokens + maxTokens;\n\n // Get tier configs (use agentic tiers if routing decided to use them)\n const useAgenticTiers =\n routingDecision.reasoning?.includes(\"agentic\") && routerOpts.config.agenticTiers;\n const tierConfigs = useAgenticTiers\n ? routerOpts.config.agenticTiers!\n : routerOpts.config.tiers;\n\n // Get full chain first, then filter by context\n const fullChain = getFallbackChain(routingDecision.tier, tierConfigs);\n const contextFiltered = getFallbackChainFiltered(\n routingDecision.tier,\n tierConfigs,\n estimatedTotalTokens,\n getModelContextWindow,\n );\n\n // Log if models were filtered out due to context limits\n const contextExcluded = fullChain.filter((m) => !contextFiltered.includes(m));\n if (contextExcluded.length > 0) {\n console.log(\n `[ClawRouter] Context filter (~${estimatedTotalTokens} tokens): excluded ${contextExcluded.join(\", \")}`,\n );\n }\n\n // Limit to MAX_FALLBACK_ATTEMPTS to prevent infinite loops\n modelsToTry = contextFiltered.slice(0, MAX_FALLBACK_ATTEMPTS);\n\n // Deprioritize rate-limited models (put them at the end)\n modelsToTry = prioritizeNonRateLimited(modelsToTry);\n } else {\n // For explicit model requests, add free model as emergency fallback\n // in case the primary model fails due to insufficient funds mid-request\n if (modelId && modelId !== FREE_MODEL) {\n modelsToTry = [modelId, FREE_MODEL];\n } else {\n modelsToTry = modelId ? [modelId] : [];\n }\n }\n\n // --- Fallback loop: try each model until success ---\n let upstream: Response | undefined;\n let lastError: { body: string; status: number } | undefined;\n let actualModelUsed = modelId;\n\n for (let i = 0; i < modelsToTry.length; i++) {\n const tryModel = modelsToTry[i];\n const isLastAttempt = i === modelsToTry.length - 1;\n\n console.log(`[ClawRouter] Trying model ${i + 1}/${modelsToTry.length}: ${tryModel}`);\n\n const result = await tryModelRequest(\n upstreamUrl,\n req.method ?? \"POST\",\n headers,\n body,\n tryModel,\n maxTokens,\n payFetch,\n balanceMonitor,\n controller.signal,\n );\n\n if (result.success && result.response) {\n upstream = result.response;\n actualModelUsed = tryModel;\n console.log(`[ClawRouter] Success with model: ${tryModel}`);\n break;\n }\n\n // Request failed\n lastError = {\n body: result.errorBody || \"Unknown error\",\n status: result.errorStatus || 500,\n };\n\n // If it's a provider error and not the last attempt, try next model\n if (result.isProviderError && !isLastAttempt) {\n // Track 429 rate limits to deprioritize this model for future requests\n if (result.errorStatus === 429) {\n markRateLimited(tryModel);\n }\n console.log(\n `[ClawRouter] Provider error from ${tryModel}, trying fallback: ${result.errorBody?.slice(0, 100)}`,\n );\n continue;\n }\n\n // Not a provider error or last attempt — stop trying\n if (!result.isProviderError) {\n console.log(\n `[ClawRouter] Non-provider error from ${tryModel}, not retrying: ${result.errorBody?.slice(0, 100)}`,\n );\n }\n break;\n }\n\n // Clear timeout — request attempts completed\n clearTimeout(timeoutId);\n\n // Clear heartbeat — real data is about to flow\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n\n // Update routing decision with actual model used (for logging)\n // IMPORTANT: Recalculate cost for the actual model, not the original primary\n if (routingDecision && actualModelUsed !== routingDecision.model) {\n const estimatedInputTokens = Math.ceil(body.length / 4);\n const newCosts = calculateModelCost(\n actualModelUsed,\n routerOpts.modelPricing,\n estimatedInputTokens,\n maxTokens,\n );\n routingDecision = {\n ...routingDecision,\n model: actualModelUsed,\n reasoning: `${routingDecision.reasoning} | fallback to ${actualModelUsed}`,\n costEstimate: newCosts.costEstimate,\n baselineCost: newCosts.baselineCost,\n savings: newCosts.savings,\n };\n options.onRouted?.(routingDecision);\n }\n\n // --- Handle case where all models failed ---\n if (!upstream) {\n const rawErrBody = lastError?.body || \"All models in fallback chain failed\";\n const errStatus = lastError?.status || 502;\n\n // Transform payment errors into user-friendly messages\n const transformedErr = transformPaymentError(rawErrBody);\n\n if (headersSentEarly) {\n // Streaming: send error as SSE event\n // If transformed error is already JSON, parse and use it; otherwise wrap in standard format\n let errPayload: string;\n try {\n const parsed = JSON.parse(transformedErr);\n errPayload = JSON.stringify(parsed);\n } catch {\n errPayload = JSON.stringify({\n error: { message: rawErrBody, type: \"provider_error\", status: errStatus },\n });\n }\n const errEvent = `data: ${errPayload}\\n\\n`;\n safeWrite(res, errEvent);\n safeWrite(res, \"data: [DONE]\\n\\n\");\n res.end();\n\n const errBuf = Buffer.from(errEvent + \"data: [DONE]\\n\\n\");\n deduplicator.complete(dedupKey, {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n body: errBuf,\n completedAt: Date.now(),\n });\n } else {\n // Non-streaming: send transformed error response\n res.writeHead(errStatus, { \"Content-Type\": \"application/json\" });\n res.end(transformedErr);\n\n deduplicator.complete(dedupKey, {\n status: errStatus,\n headers: { \"content-type\": \"application/json\" },\n body: Buffer.from(transformedErr),\n completedAt: Date.now(),\n });\n }\n return;\n }\n\n // --- Stream response and collect for dedup cache ---\n const responseChunks: Buffer[] = [];\n\n if (headersSentEarly) {\n // Streaming: headers already sent. Response should be 200 at this point\n // (non-200 responses are handled in the fallback loop above)\n\n // Convert non-streaming JSON response to SSE streaming format for client\n // (BlockRun API returns JSON since we forced stream:false)\n // OpenClaw expects: object=\"chat.completion.chunk\" with choices[].delta (not message)\n // We emit proper incremental deltas to match OpenAI's streaming format exactly\n if (upstream.body) {\n const reader = upstream.body.getReader();\n const chunks: Uint8Array[] = [];\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n // Combine chunks and transform to streaming format\n const jsonBody = Buffer.concat(chunks);\n const jsonStr = jsonBody.toString();\n try {\n const rsp = JSON.parse(jsonStr) as {\n id?: string;\n object?: string;\n created?: number;\n model?: string;\n choices?: Array<{\n index?: number;\n message?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n id: string;\n type: string;\n function: { name: string; arguments: string };\n }>;\n };\n delta?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n id: string;\n type: string;\n function: { name: string; arguments: string };\n }>;\n };\n finish_reason?: string | null;\n }>;\n usage?: unknown;\n };\n\n // Build base chunk structure (reused for all chunks)\n // Match OpenAI's exact format including system_fingerprint\n const baseChunk = {\n id: rsp.id ?? `chatcmpl-${Date.now()}`,\n object: \"chat.completion.chunk\",\n created: rsp.created ?? Math.floor(Date.now() / 1000),\n model: rsp.model ?? \"unknown\",\n system_fingerprint: null,\n };\n\n // Process each choice (usually just one)\n if (rsp.choices && Array.isArray(rsp.choices)) {\n for (const choice of rsp.choices) {\n // Strip thinking tokens (Kimi <|...|> and standard <think> tags)\n const rawContent = choice.message?.content ?? choice.delta?.content ?? \"\";\n const content = stripThinkingTokens(rawContent);\n const role = choice.message?.role ?? choice.delta?.role ?? \"assistant\";\n const index = choice.index ?? 0;\n\n // Chunk 1: role only (mimics OpenAI's first chunk)\n const roleChunk = {\n ...baseChunk,\n choices: [{ index, delta: { role }, logprobs: null, finish_reason: null }],\n };\n const roleData = `data: ${JSON.stringify(roleChunk)}\\n\\n`;\n safeWrite(res, roleData);\n responseChunks.push(Buffer.from(roleData));\n\n // Chunk 2: content (single chunk with full content)\n if (content) {\n const contentChunk = {\n ...baseChunk,\n choices: [{ index, delta: { content }, logprobs: null, finish_reason: null }],\n };\n const contentData = `data: ${JSON.stringify(contentChunk)}\\n\\n`;\n safeWrite(res, contentData);\n responseChunks.push(Buffer.from(contentData));\n }\n\n // Chunk 2b: tool_calls (forward tool calls from upstream)\n const toolCalls = choice.message?.tool_calls ?? choice.delta?.tool_calls;\n if (toolCalls && toolCalls.length > 0) {\n const toolCallChunk = {\n ...baseChunk,\n choices: [\n {\n index,\n delta: { tool_calls: toolCalls },\n logprobs: null,\n finish_reason: null,\n },\n ],\n };\n const toolCallData = `data: ${JSON.stringify(toolCallChunk)}\\n\\n`;\n safeWrite(res, toolCallData);\n responseChunks.push(Buffer.from(toolCallData));\n }\n\n // Chunk 3: finish_reason (signals completion)\n const finishChunk = {\n ...baseChunk,\n choices: [\n {\n index,\n delta: {},\n logprobs: null,\n finish_reason:\n toolCalls && toolCalls.length > 0\n ? \"tool_calls\"\n : (choice.finish_reason ?? \"stop\"),\n },\n ],\n };\n const finishData = `data: ${JSON.stringify(finishChunk)}\\n\\n`;\n safeWrite(res, finishData);\n responseChunks.push(Buffer.from(finishData));\n }\n }\n } catch {\n // If parsing fails, send raw response as single chunk\n const sseData = `data: ${jsonStr}\\n\\n`;\n safeWrite(res, sseData);\n responseChunks.push(Buffer.from(sseData));\n }\n }\n\n // Send SSE terminator\n safeWrite(res, \"data: [DONE]\\n\\n\");\n responseChunks.push(Buffer.from(\"data: [DONE]\\n\\n\"));\n res.end();\n\n // Cache for dedup\n deduplicator.complete(dedupKey, {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n body: Buffer.concat(responseChunks),\n completedAt: Date.now(),\n });\n } else {\n // Non-streaming: forward status and headers from upstream\n const responseHeaders: Record<string, string> = {};\n upstream.headers.forEach((value, key) => {\n // Skip hop-by-hop headers and content-encoding (fetch already decompresses)\n if (key === \"transfer-encoding\" || key === \"connection\" || key === \"content-encoding\")\n return;\n responseHeaders[key] = value;\n });\n\n res.writeHead(upstream.status, responseHeaders);\n\n if (upstream.body) {\n const reader = upstream.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunk = Buffer.from(value);\n safeWrite(res, chunk);\n responseChunks.push(chunk);\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n res.end();\n\n // Cache for dedup\n deduplicator.complete(dedupKey, {\n status: upstream.status,\n headers: responseHeaders,\n body: Buffer.concat(responseChunks),\n completedAt: Date.now(),\n });\n }\n\n // --- Optimistic balance deduction after successful response ---\n if (estimatedCostMicros !== undefined) {\n balanceMonitor.deductEstimated(estimatedCostMicros);\n }\n\n // Mark request as completed (for client disconnect cleanup)\n completed = true;\n } catch (err) {\n // Clear timeout on error\n clearTimeout(timeoutId);\n\n // Clear heartbeat on error\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n\n // Remove in-flight entry so retries aren't blocked\n deduplicator.removeInflight(dedupKey);\n\n // Invalidate balance cache on payment failure (might be out of date)\n balanceMonitor.invalidate();\n\n // Convert abort error to more descriptive timeout error\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new Error(`Request timed out after ${timeoutMs}ms`);\n }\n\n throw err;\n }\n\n // --- Usage logging (fire-and-forget) ---\n // Note: Recalculate cost using full body length (not just system+user message)\n // and apply 20% buffer to match actual x402 payment (see estimateAmount())\n if (routingDecision) {\n // Use full body length for accurate cost (matches x402 payment estimation)\n const estimatedInputTokens = Math.ceil(body.length / 4);\n const accurateCosts = calculateModelCost(\n routingDecision.model,\n routerOpts.modelPricing,\n estimatedInputTokens,\n maxTokens,\n );\n // Apply 20% buffer to match x402 pre-auth\n const costWithBuffer = accurateCosts.costEstimate * 1.2;\n const baselineWithBuffer = accurateCosts.baselineCost * 1.2;\n const entry: UsageEntry = {\n timestamp: new Date().toISOString(),\n model: routingDecision.model,\n tier: routingDecision.tier,\n cost: costWithBuffer,\n baselineCost: baselineWithBuffer,\n savings: accurateCosts.savings,\n latencyMs: Date.now() - startTime,\n };\n logUsage(entry).catch(() => {});\n }\n}\n","/**\n * x402 Payment Implementation\n *\n * Based on BlockRun's proven implementation.\n * Handles 402 Payment Required responses with EIP-712 signed USDC transfers.\n *\n * Optimizations (v0.3.0):\n * - Payment cache: after first 402, caches {payTo, asset, network} per endpoint.\n * On subsequent requests, pre-signs payment and sends with first request,\n * skipping the 402 round trip (~200ms savings).\n * - Falls back to normal 402 flow if pre-signed payment is rejected.\n */\n\nimport { signTypedData, privateKeyToAccount } from \"viem/accounts\";\nimport { PaymentCache } from \"./payment-cache.js\";\n\nconst BASE_CHAIN_ID = 8453;\nconst USDC_BASE = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\" as const;\n\nconst USDC_DOMAIN = {\n name: \"USD Coin\",\n version: \"2\",\n chainId: BASE_CHAIN_ID,\n verifyingContract: USDC_BASE,\n} as const;\n\nconst TRANSFER_TYPES = {\n TransferWithAuthorization: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n ],\n} as const;\n\nfunction createNonce(): `0x${string}` {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}` as `0x${string}`;\n}\n\ninterface PaymentOption {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: { name?: string; version?: string };\n}\n\ninterface PaymentRequired {\n accepts: PaymentOption[];\n resource?: { url?: string; description?: string };\n}\n\nfunction parsePaymentRequired(headerValue: string): PaymentRequired {\n const decoded = atob(headerValue);\n return JSON.parse(decoded) as PaymentRequired;\n}\n\nasync function createPaymentPayload(\n privateKey: `0x${string}`,\n fromAddress: string,\n recipient: string,\n amount: string,\n resourceUrl: string,\n): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const validAfter = now - 600;\n const validBefore = now + 300;\n const nonce = createNonce();\n\n const signature = await signTypedData({\n privateKey,\n domain: USDC_DOMAIN,\n types: TRANSFER_TYPES,\n primaryType: \"TransferWithAuthorization\",\n message: {\n from: fromAddress as `0x${string}`,\n to: recipient as `0x${string}`,\n value: BigInt(amount),\n validAfter: BigInt(validAfter),\n validBefore: BigInt(validBefore),\n nonce,\n },\n });\n\n const paymentData = {\n x402Version: 2,\n resource: {\n url: resourceUrl,\n description: \"BlockRun AI API call\",\n mimeType: \"application/json\",\n },\n accepted: {\n scheme: \"exact\",\n network: \"eip155:8453\",\n amount,\n asset: USDC_BASE,\n payTo: recipient,\n maxTimeoutSeconds: 300,\n extra: { name: \"USD Coin\", version: \"2\" },\n },\n payload: {\n signature,\n authorization: {\n from: fromAddress,\n to: recipient,\n value: amount,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n },\n },\n extensions: {},\n };\n\n return btoa(JSON.stringify(paymentData));\n}\n\n/** Pre-auth parameters for skipping the 402 round trip. */\nexport type PreAuthParams = {\n estimatedAmount: string; // USDC amount in smallest unit (6 decimals)\n};\n\n/** Result from createPaymentFetch — includes the fetch wrapper and payment cache. */\nexport type PaymentFetchResult = {\n fetch: (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ) => Promise<Response>;\n cache: PaymentCache;\n};\n\n/**\n * Create a fetch wrapper that handles x402 payment automatically.\n *\n * Supports pre-auth: if cached payment params + estimated amount are available,\n * pre-signs and attaches payment to the first request, skipping the 402 round trip.\n * Falls back to normal 402 flow if pre-signed payment is rejected.\n */\nexport function createPaymentFetch(privateKey: `0x${string}`): PaymentFetchResult {\n const account = privateKeyToAccount(privateKey);\n const walletAddress = account.address;\n const paymentCache = new PaymentCache();\n\n const payFetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const endpointPath = new URL(url).pathname;\n\n // --- Pre-auth path: skip 402 round trip ---\n const cached = paymentCache.get(endpointPath);\n if (cached && preAuth?.estimatedAmount) {\n const paymentPayload = await createPaymentPayload(\n privateKey,\n walletAddress,\n cached.payTo,\n preAuth.estimatedAmount,\n url,\n );\n\n const preAuthHeaders = new Headers(init?.headers);\n preAuthHeaders.set(\"payment-signature\", paymentPayload);\n\n const response = await fetch(input, { ...init, headers: preAuthHeaders });\n\n // Pre-auth accepted — skip 402 entirely\n if (response.status !== 402) {\n return response;\n }\n\n // Pre-auth rejected (wrong amount, payTo changed, etc.)\n // Try to use this 402's payment header for a proper retry\n const paymentHeader = response.headers.get(\"x-payment-required\");\n if (paymentHeader) {\n return handle402(input, init, url, endpointPath, paymentHeader);\n }\n\n // No payment header — invalidate cache and retry clean (no payment header)\n // to get a proper 402 with payment requirements\n paymentCache.invalidate(endpointPath);\n const cleanResponse = await fetch(input, init);\n if (cleanResponse.status !== 402) {\n return cleanResponse;\n }\n const cleanHeader = cleanResponse.headers.get(\"x-payment-required\");\n if (!cleanHeader) {\n throw new Error(\"402 response missing x-payment-required header\");\n }\n return handle402(input, init, url, endpointPath, cleanHeader);\n }\n\n // --- Normal path: first request may get 402 ---\n const response = await fetch(input, init);\n\n if (response.status !== 402) {\n return response;\n }\n\n const paymentHeader = response.headers.get(\"x-payment-required\");\n if (!paymentHeader) {\n throw new Error(\"402 response missing x-payment-required header\");\n }\n\n return handle402(input, init, url, endpointPath, paymentHeader);\n };\n\n /** Handle a 402 response: parse, cache params, sign, retry. */\n async function handle402(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n url: string,\n endpointPath: string,\n paymentHeader: string,\n ): Promise<Response> {\n const paymentRequired = parsePaymentRequired(paymentHeader);\n const option = paymentRequired.accepts?.[0];\n if (!option) {\n throw new Error(\"No payment options in 402 response\");\n }\n\n const amount = option.amount || option.maxAmountRequired;\n if (!amount) {\n throw new Error(\"No amount in payment requirements\");\n }\n\n // Cache payment params for future pre-auth\n paymentCache.set(endpointPath, {\n payTo: option.payTo,\n asset: option.asset,\n scheme: option.scheme,\n network: option.network,\n extra: option.extra,\n maxTimeoutSeconds: option.maxTimeoutSeconds,\n });\n\n // Create signed payment\n const paymentPayload = await createPaymentPayload(\n privateKey,\n walletAddress,\n option.payTo,\n amount,\n url,\n );\n\n // Retry with payment\n const retryHeaders = new Headers(init?.headers);\n retryHeaders.set(\"payment-signature\", paymentPayload);\n\n return fetch(input, {\n ...init,\n headers: retryHeaders,\n });\n }\n\n return { fetch: payFetch, cache: paymentCache };\n}\n","/**\n * Payment Parameter Cache\n *\n * Caches the 402 payment parameters (payTo, asset, network, etc.) after the first\n * request to each endpoint. On subsequent requests, pre-signs the payment and\n * attaches it to the first request, skipping the 402 round trip (~200ms savings).\n */\n\nexport type CachedPaymentParams = {\n payTo: string;\n asset: string;\n scheme: string;\n network: string;\n extra?: { name?: string; version?: string };\n maxTimeoutSeconds?: number;\n cachedAt: number;\n};\n\nconst DEFAULT_TTL_MS = 3_600_000; // 1 hour\n\nexport class PaymentCache {\n private cache = new Map<string, CachedPaymentParams>();\n private ttlMs: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS) {\n this.ttlMs = ttlMs;\n }\n\n /** Get cached payment params for an endpoint path. */\n get(endpointPath: string): CachedPaymentParams | undefined {\n const entry = this.cache.get(endpointPath);\n if (!entry) return undefined;\n if (Date.now() - entry.cachedAt > this.ttlMs) {\n this.cache.delete(endpointPath);\n return undefined;\n }\n return entry;\n }\n\n /** Cache payment params from a 402 response. */\n set(endpointPath: string, params: Omit<CachedPaymentParams, \"cachedAt\">): void {\n this.cache.set(endpointPath, { ...params, cachedAt: Date.now() });\n }\n\n /** Invalidate cache for an endpoint (e.g., if payTo changed). */\n invalidate(endpointPath: string): void {\n this.cache.delete(endpointPath);\n }\n}\n","/**\n * Rule-Based Classifier (v2 — Weighted Scoring)\n *\n * Scores a request across 14 weighted dimensions and maps the aggregate\n * score to a tier using configurable boundaries. Confidence is calibrated\n * via sigmoid — low confidence triggers the fallback classifier.\n *\n * Handles 70-80% of requests in < 1ms with zero cost.\n */\n\nimport type { Tier, ScoringResult, ScoringConfig } from \"./types.js\";\n\ntype DimensionScore = { name: string; score: number; signal: string | null };\n\n// ─── Dimension Scorers ───\n// Each returns a score in [-1, 1] and an optional signal string.\n\nfunction scoreTokenCount(\n estimatedTokens: number,\n thresholds: { simple: number; complex: number },\n): DimensionScore {\n if (estimatedTokens < thresholds.simple) {\n return { name: \"tokenCount\", score: -1.0, signal: `short (${estimatedTokens} tokens)` };\n }\n if (estimatedTokens > thresholds.complex) {\n return { name: \"tokenCount\", score: 1.0, signal: `long (${estimatedTokens} tokens)` };\n }\n return { name: \"tokenCount\", score: 0, signal: null };\n}\n\nfunction scoreKeywordMatch(\n text: string,\n keywords: string[],\n name: string,\n signalLabel: string,\n thresholds: { low: number; high: number },\n scores: { none: number; low: number; high: number },\n): DimensionScore {\n const matches = keywords.filter((kw) => text.includes(kw.toLowerCase()));\n if (matches.length >= thresholds.high) {\n return {\n name,\n score: scores.high,\n signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})`,\n };\n }\n if (matches.length >= thresholds.low) {\n return {\n name,\n score: scores.low,\n signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})`,\n };\n }\n return { name, score: scores.none, signal: null };\n}\n\nfunction scoreMultiStep(text: string): DimensionScore {\n const patterns = [/first.*then/i, /step \\d/i, /\\d\\.\\s/];\n const hits = patterns.filter((p) => p.test(text));\n if (hits.length > 0) {\n return { name: \"multiStepPatterns\", score: 0.5, signal: \"multi-step\" };\n }\n return { name: \"multiStepPatterns\", score: 0, signal: null };\n}\n\nfunction scoreQuestionComplexity(prompt: string): DimensionScore {\n const count = (prompt.match(/\\?/g) || []).length;\n if (count > 3) {\n return { name: \"questionComplexity\", score: 0.5, signal: `${count} questions` };\n }\n return { name: \"questionComplexity\", score: 0, signal: null };\n}\n\n/**\n * Score agentic task indicators.\n * Returns agenticScore (0-1) based on keyword matches:\n * - 4+ matches = 1.0 (high agentic)\n * - 3 matches = 0.6 (moderate agentic, triggers auto-agentic mode)\n * - 1-2 matches = 0.2 (low agentic)\n *\n * Thresholds raised because common keywords were pruned from the list.\n */\nfunction scoreAgenticTask(\n text: string,\n keywords: string[],\n): { dimensionScore: DimensionScore; agenticScore: number } {\n let matchCount = 0;\n const signals: string[] = [];\n\n for (const keyword of keywords) {\n if (text.includes(keyword.toLowerCase())) {\n matchCount++;\n if (signals.length < 3) {\n signals.push(keyword);\n }\n }\n }\n\n // Threshold-based scoring (raised thresholds after keyword pruning)\n if (matchCount >= 4) {\n return {\n dimensionScore: {\n name: \"agenticTask\",\n score: 1.0,\n signal: `agentic (${signals.join(\", \")})`,\n },\n agenticScore: 1.0,\n };\n } else if (matchCount >= 3) {\n return {\n dimensionScore: {\n name: \"agenticTask\",\n score: 0.6,\n signal: `agentic (${signals.join(\", \")})`,\n },\n agenticScore: 0.6,\n };\n } else if (matchCount >= 1) {\n return {\n dimensionScore: {\n name: \"agenticTask\",\n score: 0.2,\n signal: `agentic-light (${signals.join(\", \")})`,\n },\n agenticScore: 0.2,\n };\n }\n\n return {\n dimensionScore: { name: \"agenticTask\", score: 0, signal: null },\n agenticScore: 0,\n };\n}\n\n// ─── Main Classifier ───\n\nexport function classifyByRules(\n prompt: string,\n systemPrompt: string | undefined,\n estimatedTokens: number,\n config: ScoringConfig,\n): ScoringResult {\n const text = `${systemPrompt ?? \"\"} ${prompt}`.toLowerCase();\n // User prompt only — used for reasoning markers (system prompt shouldn't influence complexity)\n const userText = prompt.toLowerCase();\n\n // Score all 14 dimensions\n const dimensions: DimensionScore[] = [\n // Original 8 dimensions\n scoreTokenCount(estimatedTokens, config.tokenCountThresholds),\n scoreKeywordMatch(\n text,\n config.codeKeywords,\n \"codePresence\",\n \"code\",\n { low: 1, high: 2 },\n { none: 0, low: 0.5, high: 1.0 },\n ),\n // Reasoning markers use USER prompt only — system prompt \"step by step\" shouldn't trigger reasoning\n scoreKeywordMatch(\n userText,\n config.reasoningKeywords,\n \"reasoningMarkers\",\n \"reasoning\",\n { low: 1, high: 2 },\n { none: 0, low: 0.7, high: 1.0 },\n ),\n scoreKeywordMatch(\n text,\n config.technicalKeywords,\n \"technicalTerms\",\n \"technical\",\n { low: 2, high: 4 },\n { none: 0, low: 0.5, high: 1.0 },\n ),\n scoreKeywordMatch(\n text,\n config.creativeKeywords,\n \"creativeMarkers\",\n \"creative\",\n { low: 1, high: 2 },\n { none: 0, low: 0.5, high: 0.7 },\n ),\n scoreKeywordMatch(\n text,\n config.simpleKeywords,\n \"simpleIndicators\",\n \"simple\",\n { low: 1, high: 2 },\n { none: 0, low: -1.0, high: -1.0 },\n ),\n scoreMultiStep(text),\n scoreQuestionComplexity(prompt),\n\n // 6 new dimensions\n scoreKeywordMatch(\n text,\n config.imperativeVerbs,\n \"imperativeVerbs\",\n \"imperative\",\n { low: 1, high: 2 },\n { none: 0, low: 0.3, high: 0.5 },\n ),\n scoreKeywordMatch(\n text,\n config.constraintIndicators,\n \"constraintCount\",\n \"constraints\",\n { low: 1, high: 3 },\n { none: 0, low: 0.3, high: 0.7 },\n ),\n scoreKeywordMatch(\n text,\n config.outputFormatKeywords,\n \"outputFormat\",\n \"format\",\n { low: 1, high: 2 },\n { none: 0, low: 0.4, high: 0.7 },\n ),\n scoreKeywordMatch(\n text,\n config.referenceKeywords,\n \"referenceComplexity\",\n \"references\",\n { low: 1, high: 2 },\n { none: 0, low: 0.3, high: 0.5 },\n ),\n scoreKeywordMatch(\n text,\n config.negationKeywords,\n \"negationComplexity\",\n \"negation\",\n { low: 2, high: 3 },\n { none: 0, low: 0.3, high: 0.5 },\n ),\n scoreKeywordMatch(\n text,\n config.domainSpecificKeywords,\n \"domainSpecificity\",\n \"domain-specific\",\n { low: 1, high: 2 },\n { none: 0, low: 0.5, high: 0.8 },\n ),\n ];\n\n // Score agentic task indicators\n const agenticResult = scoreAgenticTask(text, config.agenticTaskKeywords);\n dimensions.push(agenticResult.dimensionScore);\n const agenticScore = agenticResult.agenticScore;\n\n // Collect signals\n const signals = dimensions.filter((d) => d.signal !== null).map((d) => d.signal!);\n\n // Compute weighted score\n const weights = config.dimensionWeights;\n let weightedScore = 0;\n for (const d of dimensions) {\n const w = weights[d.name] ?? 0;\n weightedScore += d.score * w;\n }\n\n // Count reasoning markers for override — only check USER prompt, not system prompt\n // This prevents system prompts with \"step by step\" from triggering REASONING for simple queries\n const reasoningMatches = config.reasoningKeywords.filter((kw) =>\n userText.includes(kw.toLowerCase()),\n );\n\n // Direct reasoning override: 2+ reasoning markers = high confidence REASONING\n if (reasoningMatches.length >= 2) {\n const confidence = calibrateConfidence(\n Math.max(weightedScore, 0.3), // ensure positive for confidence calc\n config.confidenceSteepness,\n );\n return {\n score: weightedScore,\n tier: \"REASONING\",\n confidence: Math.max(confidence, 0.85),\n signals,\n agenticScore,\n };\n }\n\n // Map weighted score to tier using boundaries\n const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;\n let tier: Tier;\n let distanceFromBoundary: number;\n\n if (weightedScore < simpleMedium) {\n tier = \"SIMPLE\";\n distanceFromBoundary = simpleMedium - weightedScore;\n } else if (weightedScore < mediumComplex) {\n tier = \"MEDIUM\";\n distanceFromBoundary = Math.min(weightedScore - simpleMedium, mediumComplex - weightedScore);\n } else if (weightedScore < complexReasoning) {\n tier = \"COMPLEX\";\n distanceFromBoundary = Math.min(\n weightedScore - mediumComplex,\n complexReasoning - weightedScore,\n );\n } else {\n tier = \"REASONING\";\n distanceFromBoundary = weightedScore - complexReasoning;\n }\n\n // Calibrate confidence via sigmoid of distance from nearest boundary\n const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);\n\n // If confidence is below threshold → ambiguous\n if (confidence < config.confidenceThreshold) {\n return { score: weightedScore, tier: null, confidence, signals, agenticScore };\n }\n\n return { score: weightedScore, tier, confidence, signals, agenticScore };\n}\n\n/**\n * Sigmoid confidence calibration.\n * Maps distance from tier boundary to [0.5, 1.0] confidence range.\n */\nfunction calibrateConfidence(distance: number, steepness: number): number {\n return 1 / (1 + Math.exp(-steepness * distance));\n}\n","/**\n * Tier → Model Selection\n *\n * Maps a classification tier to the cheapest capable model.\n * Builds RoutingDecision metadata with cost estimates and savings.\n */\n\nimport type { Tier, TierConfig, RoutingDecision } from \"./types.js\";\n\nexport type ModelPricing = {\n inputPrice: number; // per 1M tokens\n outputPrice: number; // per 1M tokens\n};\n\n/**\n * Select the primary model for a tier and build the RoutingDecision.\n */\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n): RoutingDecision {\n const tierConfig = tierConfigs[tier];\n const model = tierConfig.primary;\n const pricing = modelPricing.get(model);\n\n // Defensive: guard against undefined price fields (not just undefined pricing)\n const inputPrice = pricing?.inputPrice ?? 0;\n const outputPrice = pricing?.outputPrice ?? 0;\n const inputCost = (estimatedInputTokens / 1_000_000) * inputPrice;\n const outputCost = (maxOutputTokens / 1_000_000) * outputPrice;\n const costEstimate = inputCost + outputCost;\n\n // Baseline: what Claude Opus would cost (the premium default)\n const opusPricing = modelPricing.get(\"anthropic/claude-opus-4\");\n const opusInputPrice = opusPricing?.inputPrice ?? 0;\n const opusOutputPrice = opusPricing?.outputPrice ?? 0;\n const baselineInput = (estimatedInputTokens / 1_000_000) * opusInputPrice;\n const baselineOutput = (maxOutputTokens / 1_000_000) * opusOutputPrice;\n const baselineCost = baselineInput + baselineOutput;\n\n const savings = baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;\n\n return {\n model,\n tier,\n confidence,\n method,\n reasoning,\n costEstimate,\n baselineCost,\n savings,\n };\n}\n\n/**\n * Get the ordered fallback chain for a tier: [primary, ...fallbacks].\n */\nexport function getFallbackChain(tier: Tier, tierConfigs: Record<Tier, TierConfig>): string[] {\n const config = tierConfigs[tier];\n return [config.primary, ...config.fallback];\n}\n\n/**\n * Calculate cost for a specific model (used when fallback model is used).\n * Returns updated cost fields for RoutingDecision.\n */\nexport function calculateModelCost(\n model: string,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n): { costEstimate: number; baselineCost: number; savings: number } {\n const pricing = modelPricing.get(model);\n\n // Defensive: guard against undefined price fields (not just undefined pricing)\n const inputPrice = pricing?.inputPrice ?? 0;\n const outputPrice = pricing?.outputPrice ?? 0;\n const inputCost = (estimatedInputTokens / 1_000_000) * inputPrice;\n const outputCost = (maxOutputTokens / 1_000_000) * outputPrice;\n const costEstimate = inputCost + outputCost;\n\n // Baseline: what Claude Opus would cost\n const opusPricing = modelPricing.get(\"anthropic/claude-opus-4\");\n const opusInputPrice = opusPricing?.inputPrice ?? 0;\n const opusOutputPrice = opusPricing?.outputPrice ?? 0;\n const baselineInput = (estimatedInputTokens / 1_000_000) * opusInputPrice;\n const baselineOutput = (maxOutputTokens / 1_000_000) * opusOutputPrice;\n const baselineCost = baselineInput + baselineOutput;\n\n const savings = baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;\n\n return { costEstimate, baselineCost, savings };\n}\n\n/**\n * Get the fallback chain filtered by context length.\n * Only returns models that can handle the estimated total context.\n *\n * @param tier - The tier to get fallback chain for\n * @param tierConfigs - Tier configurations\n * @param estimatedTotalTokens - Estimated total context (input + output)\n * @param getContextWindow - Function to get context window for a model ID\n * @returns Filtered list of models that can handle the context\n */\nexport function getFallbackChainFiltered(\n tier: Tier,\n tierConfigs: Record<Tier, TierConfig>,\n estimatedTotalTokens: number,\n getContextWindow: (modelId: string) => number | undefined,\n): string[] {\n const fullChain = getFallbackChain(tier, tierConfigs);\n\n // Filter to models that can handle the context\n const filtered = fullChain.filter((modelId) => {\n const contextWindow = getContextWindow(modelId);\n if (contextWindow === undefined) {\n // Unknown model - include it (let API reject if needed)\n return true;\n }\n // Add 10% buffer for safety\n return contextWindow >= estimatedTotalTokens * 1.1;\n });\n\n // If all models filtered out, return the original chain\n // (let the API error out - better than no options)\n if (filtered.length === 0) {\n return fullChain;\n }\n\n return filtered;\n}\n","/**\n * Default Routing Config\n *\n * All routing parameters as a TypeScript constant.\n * Operators override via openclaw.yaml plugin config.\n *\n * Scoring uses 14 weighted dimensions with sigmoid confidence calibration.\n */\n\nimport type { RoutingConfig } from \"./types.js\";\n\nexport const DEFAULT_ROUTING_CONFIG: RoutingConfig = {\n version: \"2.0\",\n\n classifier: {\n llmModel: \"google/gemini-2.5-flash\",\n llmMaxTokens: 10,\n llmTemperature: 0,\n promptTruncationChars: 500,\n cacheTtlMs: 3_600_000, // 1 hour\n },\n\n scoring: {\n tokenCountThresholds: { simple: 50, complex: 500 },\n\n // Multilingual keywords: English + Chinese (中文) + Japanese (日本語) + Russian (Русский) + German (Deutsch)\n codeKeywords: [\n // English\n \"function\",\n \"class\",\n \"import\",\n \"def\",\n \"SELECT\",\n \"async\",\n \"await\",\n \"const\",\n \"let\",\n \"var\",\n \"return\",\n \"```\",\n // Chinese\n \"函数\",\n \"类\",\n \"导入\",\n \"定义\",\n \"查询\",\n \"异步\",\n \"等待\",\n \"常量\",\n \"变量\",\n \"返回\",\n // Japanese\n \"関数\",\n \"クラス\",\n \"インポート\",\n \"非同期\",\n \"定数\",\n \"変数\",\n // Russian\n \"функция\",\n \"класс\",\n \"импорт\",\n \"определ\",\n \"запрос\",\n \"асинхронный\",\n \"ожидать\",\n \"константа\",\n \"переменная\",\n \"вернуть\",\n // German\n \"funktion\",\n \"klasse\",\n \"importieren\",\n \"definieren\",\n \"abfrage\",\n \"asynchron\",\n \"erwarten\",\n \"konstante\",\n \"variable\",\n \"zurückgeben\",\n ],\n reasoningKeywords: [\n // English\n \"prove\",\n \"theorem\",\n \"derive\",\n \"step by step\",\n \"chain of thought\",\n \"formally\",\n \"mathematical\",\n \"proof\",\n \"logically\",\n // Chinese\n \"证明\",\n \"定理\",\n \"推导\",\n \"逐步\",\n \"思维链\",\n \"形式化\",\n \"数学\",\n \"逻辑\",\n // Japanese\n \"証明\",\n \"定理\",\n \"導出\",\n \"ステップバイステップ\",\n \"論理的\",\n // Russian\n \"доказать\",\n \"докажи\",\n \"доказательств\",\n \"теорема\",\n \"вывести\",\n \"шаг за шагом\",\n \"пошагово\",\n \"поэтапно\",\n \"цепочка рассуждений\",\n \"рассуждени\",\n \"формально\",\n \"математически\",\n \"логически\",\n // German\n \"beweisen\",\n \"beweis\",\n \"theorem\",\n \"ableiten\",\n \"schritt für schritt\",\n \"gedankenkette\",\n \"formal\",\n \"mathematisch\",\n \"logisch\",\n ],\n simpleKeywords: [\n // English\n \"what is\",\n \"define\",\n \"translate\",\n \"hello\",\n \"yes or no\",\n \"capital of\",\n \"how old\",\n \"who is\",\n \"when was\",\n // Chinese\n \"什么是\",\n \"定义\",\n \"翻译\",\n \"你好\",\n \"是否\",\n \"首都\",\n \"多大\",\n \"谁是\",\n \"何时\",\n // Japanese\n \"とは\",\n \"定義\",\n \"翻訳\",\n \"こんにちは\",\n \"はいかいいえ\",\n \"首都\",\n \"誰\",\n // Russian\n \"что такое\",\n \"определение\",\n \"перевести\",\n \"переведи\",\n \"привет\",\n \"да или нет\",\n \"столица\",\n \"сколько лет\",\n \"кто такой\",\n \"когда\",\n \"объясни\",\n // German\n \"was ist\",\n \"definiere\",\n \"übersetze\",\n \"hallo\",\n \"ja oder nein\",\n \"hauptstadt\",\n \"wie alt\",\n \"wer ist\",\n \"wann\",\n \"erkläre\",\n ],\n technicalKeywords: [\n // English\n \"algorithm\",\n \"optimize\",\n \"architecture\",\n \"distributed\",\n \"kubernetes\",\n \"microservice\",\n \"database\",\n \"infrastructure\",\n // Chinese\n \"算法\",\n \"优化\",\n \"架构\",\n \"分布式\",\n \"微服务\",\n \"数据库\",\n \"基础设施\",\n // Japanese\n \"アルゴリズム\",\n \"最適化\",\n \"アーキテクチャ\",\n \"分散\",\n \"マイクロサービス\",\n \"データベース\",\n // Russian\n \"алгоритм\",\n \"оптимизировать\",\n \"оптимизаци\",\n \"оптимизируй\",\n \"архитектура\",\n \"распределённый\",\n \"микросервис\",\n \"база данных\",\n \"инфраструктура\",\n // German\n \"algorithmus\",\n \"optimieren\",\n \"architektur\",\n \"verteilt\",\n \"kubernetes\",\n \"mikroservice\",\n \"datenbank\",\n \"infrastruktur\",\n ],\n creativeKeywords: [\n // English\n \"story\",\n \"poem\",\n \"compose\",\n \"brainstorm\",\n \"creative\",\n \"imagine\",\n \"write a\",\n // Chinese\n \"故事\",\n \"诗\",\n \"创作\",\n \"头脑风暴\",\n \"创意\",\n \"想象\",\n \"写一个\",\n // Japanese\n \"物語\",\n \"詩\",\n \"作曲\",\n \"ブレインストーム\",\n \"創造的\",\n \"想像\",\n // Russian\n \"история\",\n \"рассказ\",\n \"стихотворение\",\n \"сочинить\",\n \"сочини\",\n \"мозговой штурм\",\n \"творческий\",\n \"представить\",\n \"придумай\",\n \"напиши\",\n // German\n \"geschichte\",\n \"gedicht\",\n \"komponieren\",\n \"brainstorming\",\n \"kreativ\",\n \"vorstellen\",\n \"schreibe\",\n \"erzählung\",\n ],\n\n // New dimension keyword lists (multilingual)\n imperativeVerbs: [\n // English\n \"build\",\n \"create\",\n \"implement\",\n \"design\",\n \"develop\",\n \"construct\",\n \"generate\",\n \"deploy\",\n \"configure\",\n \"set up\",\n // Chinese\n \"构建\",\n \"创建\",\n \"实现\",\n \"设计\",\n \"开发\",\n \"生成\",\n \"部署\",\n \"配置\",\n \"设置\",\n // Japanese\n \"構築\",\n \"作成\",\n \"実装\",\n \"設計\",\n \"開発\",\n \"生成\",\n \"デプロイ\",\n \"設定\",\n // Russian\n \"построить\",\n \"построй\",\n \"создать\",\n \"создай\",\n \"реализовать\",\n \"реализуй\",\n \"спроектировать\",\n \"разработать\",\n \"разработай\",\n \"сконструировать\",\n \"сгенерировать\",\n \"сгенерируй\",\n \"развернуть\",\n \"разверни\",\n \"настроить\",\n \"настрой\",\n // German\n \"erstellen\",\n \"bauen\",\n \"implementieren\",\n \"entwerfen\",\n \"entwickeln\",\n \"konstruieren\",\n \"generieren\",\n \"bereitstellen\",\n \"konfigurieren\",\n \"einrichten\",\n ],\n constraintIndicators: [\n // English\n \"under\",\n \"at most\",\n \"at least\",\n \"within\",\n \"no more than\",\n \"o(\",\n \"maximum\",\n \"minimum\",\n \"limit\",\n \"budget\",\n // Chinese\n \"不超过\",\n \"至少\",\n \"最多\",\n \"在内\",\n \"最大\",\n \"最小\",\n \"限制\",\n \"预算\",\n // Japanese\n \"以下\",\n \"最大\",\n \"最小\",\n \"制限\",\n \"予算\",\n // Russian\n \"не более\",\n \"не менее\",\n \"как минимум\",\n \"в пределах\",\n \"максимум\",\n \"минимум\",\n \"ограничение\",\n \"бюджет\",\n // German\n \"höchstens\",\n \"mindestens\",\n \"innerhalb\",\n \"nicht mehr als\",\n \"maximal\",\n \"minimal\",\n \"grenze\",\n \"budget\",\n ],\n outputFormatKeywords: [\n // English\n \"json\",\n \"yaml\",\n \"xml\",\n \"table\",\n \"csv\",\n \"markdown\",\n \"schema\",\n \"format as\",\n \"structured\",\n // Chinese\n \"表格\",\n \"格式化为\",\n \"结构化\",\n // Japanese\n \"テーブル\",\n \"フォーマット\",\n \"構造化\",\n // Russian\n \"таблица\",\n \"форматировать как\",\n \"структурированный\",\n // German\n \"tabelle\",\n \"formatieren als\",\n \"strukturiert\",\n ],\n referenceKeywords: [\n // English\n \"above\",\n \"below\",\n \"previous\",\n \"following\",\n \"the docs\",\n \"the api\",\n \"the code\",\n \"earlier\",\n \"attached\",\n // Chinese\n \"上面\",\n \"下面\",\n \"之前\",\n \"接下来\",\n \"文档\",\n \"代码\",\n \"附件\",\n // Japanese\n \"上記\",\n \"下記\",\n \"前の\",\n \"次の\",\n \"ドキュメント\",\n \"コード\",\n // Russian\n \"выше\",\n \"ниже\",\n \"предыдущий\",\n \"следующий\",\n \"документация\",\n \"код\",\n \"ранее\",\n \"вложение\",\n // German\n \"oben\",\n \"unten\",\n \"vorherige\",\n \"folgende\",\n \"dokumentation\",\n \"der code\",\n \"früher\",\n \"anhang\",\n ],\n negationKeywords: [\n // English\n \"don't\",\n \"do not\",\n \"avoid\",\n \"never\",\n \"without\",\n \"except\",\n \"exclude\",\n \"no longer\",\n // Chinese\n \"不要\",\n \"避免\",\n \"从不\",\n \"没有\",\n \"除了\",\n \"排除\",\n // Japanese\n \"しないで\",\n \"避ける\",\n \"決して\",\n \"なしで\",\n \"除く\",\n // Russian\n \"не делай\",\n \"не надо\",\n \"нельзя\",\n \"избегать\",\n \"никогда\",\n \"без\",\n \"кроме\",\n \"исключить\",\n \"больше не\",\n // German\n \"nicht\",\n \"vermeide\",\n \"niemals\",\n \"ohne\",\n \"außer\",\n \"ausschließen\",\n \"nicht mehr\",\n ],\n domainSpecificKeywords: [\n // English\n \"quantum\",\n \"fpga\",\n \"vlsi\",\n \"risc-v\",\n \"asic\",\n \"photonics\",\n \"genomics\",\n \"proteomics\",\n \"topological\",\n \"homomorphic\",\n \"zero-knowledge\",\n \"lattice-based\",\n // Chinese\n \"量子\",\n \"光子学\",\n \"基因组学\",\n \"蛋白质组学\",\n \"拓扑\",\n \"同态\",\n \"零知识\",\n \"格密码\",\n // Japanese\n \"量子\",\n \"フォトニクス\",\n \"ゲノミクス\",\n \"トポロジカル\",\n // Russian\n \"квантовый\",\n \"фотоника\",\n \"геномика\",\n \"протеомика\",\n \"топологический\",\n \"гомоморфный\",\n \"с нулевым разглашением\",\n \"на основе решёток\",\n // German\n \"quanten\",\n \"photonik\",\n \"genomik\",\n \"proteomik\",\n \"topologisch\",\n \"homomorph\",\n \"zero-knowledge\",\n \"gitterbasiert\",\n ],\n\n // Agentic task keywords - file ops, execution, multi-step, iterative work\n // Pruned: removed overly common words like \"then\", \"first\", \"run\", \"test\", \"build\"\n agenticTaskKeywords: [\n // English - File operations (clearly agentic)\n \"read file\",\n \"read the file\",\n \"look at\",\n \"check the\",\n \"open the\",\n \"edit\",\n \"modify\",\n \"update the\",\n \"change the\",\n \"write to\",\n \"create file\",\n // English - Execution (specific commands only)\n \"execute\",\n \"deploy\",\n \"install\",\n \"npm\",\n \"pip\",\n \"compile\",\n // English - Multi-step patterns (specific only)\n \"after that\",\n \"and also\",\n \"once done\",\n \"step 1\",\n \"step 2\",\n // English - Iterative work\n \"fix\",\n \"debug\",\n \"until it works\",\n \"keep trying\",\n \"iterate\",\n \"make sure\",\n \"verify\",\n \"confirm\",\n // Chinese (keep specific ones)\n \"读取文件\",\n \"查看\",\n \"打开\",\n \"编辑\",\n \"修改\",\n \"更新\",\n \"创建\",\n \"执行\",\n \"部署\",\n \"安装\",\n \"第一步\",\n \"第二步\",\n \"修复\",\n \"调试\",\n \"直到\",\n \"确认\",\n \"验证\",\n ],\n\n // Dimension weights (sum to 1.0)\n dimensionWeights: {\n tokenCount: 0.08,\n codePresence: 0.15,\n reasoningMarkers: 0.18,\n technicalTerms: 0.1,\n creativeMarkers: 0.05,\n simpleIndicators: 0.02, // Reduced from 0.12 to make room for agenticTask\n multiStepPatterns: 0.12,\n questionComplexity: 0.05,\n imperativeVerbs: 0.03,\n constraintCount: 0.04,\n outputFormat: 0.03,\n referenceComplexity: 0.02,\n negationComplexity: 0.01,\n domainSpecificity: 0.02,\n agenticTask: 0.04, // Reduced - agentic signals influence tier selection, not dominate it\n },\n\n // Tier boundaries on weighted score axis\n tierBoundaries: {\n simpleMedium: 0.0,\n mediumComplex: 0.18,\n complexReasoning: 0.4, // Raised from 0.25 - requires strong reasoning signals\n },\n\n // Sigmoid steepness for confidence calibration\n confidenceSteepness: 12,\n // Below this confidence → ambiguous (null tier)\n confidenceThreshold: 0.7,\n },\n\n tiers: {\n SIMPLE: {\n primary: \"nvidia/kimi-k2.5\", // Ultra-cheap $0.001/$0.001\n fallback: [\n \"google/gemini-2.5-flash\",\n \"nvidia/gpt-oss-120b\",\n \"nvidia/gpt-oss-20b\",\n \"deepseek/deepseek-chat\",\n ],\n },\n MEDIUM: {\n primary: \"xai/grok-code-fast-1\", // Code specialist, $0.20/$1.50\n fallback: [\n \"xai/grok-4-1-fast-non-reasoning\", // Upgraded Grok 4.1\n \"deepseek/deepseek-chat\",\n \"google/gemini-2.5-flash\",\n ],\n },\n COMPLEX: {\n primary: \"google/gemini-2.5-pro\",\n fallback: [\"openai/gpt-5.2\", \"anthropic/claude-sonnet-4\", \"xai/grok-4-0709\", \"openai/gpt-4o\"],\n },\n REASONING: {\n primary: \"xai/grok-4-1-fast-reasoning\", // Upgraded Grok 4.1 reasoning $0.20/$0.50\n fallback: [\n \"xai/grok-4-fast-reasoning\",\n \"openai/o3\", // Strong reasoning model\n \"deepseek/deepseek-reasoner\",\n \"moonshot/kimi-k2.5\",\n ],\n },\n },\n\n // Agentic tier configs - models that excel at multi-step autonomous tasks\n agenticTiers: {\n SIMPLE: {\n primary: \"moonshot/kimi-k2.5\", // Cheaper than Haiku ($0.5/$2.4 vs $1/$5), larger context\n fallback: [\n \"anthropic/claude-haiku-4.5\",\n \"xai/grok-4-fast-non-reasoning\",\n \"openai/gpt-4o-mini\",\n ],\n },\n MEDIUM: {\n primary: \"xai/grok-code-fast-1\", // Code specialist for agentic coding\n fallback: [\"moonshot/kimi-k2.5\", \"anthropic/claude-haiku-4.5\", \"anthropic/claude-sonnet-4\"],\n },\n COMPLEX: {\n primary: \"anthropic/claude-sonnet-4\",\n fallback: [\"anthropic/claude-opus-4.5\", \"openai/gpt-5.2\", \"xai/grok-4-0709\"], // Opus 4.5 is 3x cheaper than Opus 4\n },\n REASONING: {\n primary: \"anthropic/claude-sonnet-4\", // Strong tool use + reasoning for agentic tasks\n fallback: [\"xai/grok-4-fast-reasoning\", \"moonshot/kimi-k2.5\", \"deepseek/deepseek-reasoner\"],\n },\n },\n\n overrides: {\n maxTokensForceComplex: 100_000,\n structuredOutputMinTier: \"MEDIUM\",\n ambiguousDefaultTier: \"MEDIUM\",\n agenticMode: false,\n },\n};\n","/**\n * Smart Router Entry Point\n *\n * Classifies requests and routes to the cheapest capable model.\n * 100% local — rules-based scoring handles all requests in <1ms.\n * Ambiguous cases default to configurable tier (MEDIUM by default).\n */\n\nimport type { Tier, RoutingDecision, RoutingConfig } from \"./types.js\";\nimport { classifyByRules } from \"./rules.js\";\nimport { selectModel, type ModelPricing } from \"./selector.js\";\n\nexport type RouterOptions = {\n config: RoutingConfig;\n modelPricing: Map<string, ModelPricing>;\n};\n\n/**\n * Route a request to the cheapest capable model.\n *\n * 1. Check overrides (large context, structured output)\n * 2. Run rule-based classifier (14 weighted dimensions, <1ms)\n * 3. If ambiguous, default to configurable tier (no external API calls)\n * 4. Select model for tier\n * 5. Return RoutingDecision with metadata\n */\nexport function route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n): RoutingDecision {\n const { config, modelPricing } = options;\n\n // Estimate input tokens (~4 chars per token)\n const fullText = `${systemPrompt ?? \"\"} ${prompt}`;\n const estimatedTokens = Math.ceil(fullText.length / 4);\n\n // --- Rule-based classification (runs first to get agenticScore) ---\n const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);\n\n // Determine if agentic tiers should be used:\n // 1. Explicit agenticMode config OR\n // 2. Auto-detected agentic task (agenticScore >= 0.69)\n const agenticScore = ruleResult.agenticScore ?? 0;\n const isAutoAgentic = agenticScore >= 0.69;\n const isExplicitAgentic = config.overrides.agenticMode ?? false;\n const useAgenticTiers = (isAutoAgentic || isExplicitAgentic) && config.agenticTiers != null;\n const tierConfigs = useAgenticTiers ? config.agenticTiers! : config.tiers;\n\n // --- Override: large context → force COMPLEX ---\n if (estimatedTokens > config.overrides.maxTokensForceComplex) {\n return selectModel(\n \"COMPLEX\",\n 0.95,\n \"rules\",\n `Input exceeds ${config.overrides.maxTokensForceComplex} tokens${useAgenticTiers ? \" | agentic\" : \"\"}`,\n tierConfigs,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n }\n\n // Structured output detection\n const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;\n\n let tier: Tier;\n let confidence: number;\n const method: \"rules\" | \"llm\" = \"rules\";\n let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(\", \")}`;\n\n if (ruleResult.tier !== null) {\n tier = ruleResult.tier;\n confidence = ruleResult.confidence;\n } else {\n // Ambiguous — default to configurable tier (no external API call)\n tier = config.overrides.ambiguousDefaultTier;\n confidence = 0.5;\n reasoning += ` | ambiguous -> default: ${tier}`;\n }\n\n // Apply structured output minimum tier\n if (hasStructuredOutput) {\n const tierRank: Record<Tier, number> = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };\n const minTier = config.overrides.structuredOutputMinTier;\n if (tierRank[tier] < tierRank[minTier]) {\n reasoning += ` | upgraded to ${minTier} (structured output)`;\n tier = minTier;\n }\n }\n\n // Add agentic mode indicator to reasoning\n if (isAutoAgentic) {\n reasoning += \" | auto-agentic\";\n } else if (isExplicitAgentic) {\n reasoning += \" | agentic\";\n }\n\n return selectModel(\n tier,\n confidence,\n method,\n reasoning,\n tierConfigs,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n}\n\nexport { getFallbackChain, getFallbackChainFiltered, calculateModelCost } from \"./selector.js\";\nexport { DEFAULT_ROUTING_CONFIG } from \"./config.js\";\nexport type { RoutingDecision, Tier, RoutingConfig } from \"./types.js\";\nexport type { ModelPricing } from \"./selector.js\";\n","/**\n * BlockRun Model Definitions for OpenClaw\n *\n * Maps BlockRun's 30+ AI models to OpenClaw's ModelDefinitionConfig format.\n * All models use the \"openai-completions\" API since BlockRun is OpenAI-compatible.\n *\n * Pricing is in USD per 1M tokens. Operators pay these rates via x402;\n * they set their own markup when reselling to end users (Phase 2).\n */\n\nimport type { ModelDefinitionConfig, ModelProviderConfig } from \"./types.js\";\n\n/**\n * Model aliases for convenient shorthand access.\n * Users can type `/model claude` instead of `/model blockrun/anthropic/claude-sonnet-4`.\n */\nexport const MODEL_ALIASES: Record<string, string> = {\n // Claude\n claude: \"anthropic/claude-sonnet-4\",\n sonnet: \"anthropic/claude-sonnet-4\",\n opus: \"anthropic/claude-opus-4\",\n haiku: \"anthropic/claude-haiku-4.5\",\n\n // OpenAI\n gpt: \"openai/gpt-4o\",\n gpt4: \"openai/gpt-4o\",\n gpt5: \"openai/gpt-5.2\",\n mini: \"openai/gpt-4o-mini\",\n o3: \"openai/o3\",\n\n // DeepSeek\n deepseek: \"deepseek/deepseek-chat\",\n reasoner: \"deepseek/deepseek-reasoner\",\n\n // Kimi / Moonshot\n kimi: \"moonshot/kimi-k2.5\",\n\n // Google\n gemini: \"google/gemini-2.5-pro\",\n flash: \"google/gemini-2.5-flash\",\n\n // xAI\n grok: \"xai/grok-3\",\n \"grok-fast\": \"xai/grok-4-fast-reasoning\",\n \"grok-code\": \"xai/grok-code-fast-1\",\n\n // NVIDIA (free)\n nvidia: \"nvidia/gpt-oss-120b\",\n \"gpt-120b\": \"nvidia/gpt-oss-120b\",\n \"gpt-20b\": \"nvidia/gpt-oss-20b\",\n free: \"nvidia/gpt-oss-120b\",\n};\n\n/**\n * Resolve a model alias to its full model ID.\n * Returns the original model if not an alias.\n */\nexport function resolveModelAlias(model: string): string {\n const normalized = model.trim().toLowerCase();\n const resolved = MODEL_ALIASES[normalized];\n if (resolved) return resolved;\n\n // Check with \"blockrun/\" prefix stripped\n if (normalized.startsWith(\"blockrun/\")) {\n const withoutPrefix = normalized.slice(\"blockrun/\".length);\n const resolvedWithoutPrefix = MODEL_ALIASES[withoutPrefix];\n if (resolvedWithoutPrefix) return resolvedWithoutPrefix;\n }\n\n return model;\n}\n\ntype BlockRunModel = {\n id: string;\n name: string;\n inputPrice: number;\n outputPrice: number;\n contextWindow: number;\n maxOutput: number;\n reasoning?: boolean;\n vision?: boolean;\n /** Models optimized for agentic workflows (multi-step autonomous tasks) */\n agentic?: boolean;\n};\n\nexport const BLOCKRUN_MODELS: BlockRunModel[] = [\n // Smart routing meta-model — proxy replaces with actual model\n // NOTE: Model IDs are WITHOUT provider prefix (OpenClaw adds \"blockrun/\" automatically)\n {\n id: \"auto\",\n name: \"BlockRun Smart Router\",\n inputPrice: 0,\n outputPrice: 0,\n contextWindow: 1_050_000,\n maxOutput: 128_000,\n },\n\n // OpenAI GPT-5 Family\n {\n id: \"openai/gpt-5.2\",\n name: \"GPT-5.2\",\n inputPrice: 1.75,\n outputPrice: 14.0,\n contextWindow: 400000,\n maxOutput: 128000,\n reasoning: true,\n vision: true,\n agentic: true,\n },\n {\n id: \"openai/gpt-5-mini\",\n name: \"GPT-5 Mini\",\n inputPrice: 0.25,\n outputPrice: 2.0,\n contextWindow: 200000,\n maxOutput: 65536,\n },\n {\n id: \"openai/gpt-5-nano\",\n name: \"GPT-5 Nano\",\n inputPrice: 0.05,\n outputPrice: 0.4,\n contextWindow: 128000,\n maxOutput: 32768,\n },\n {\n id: \"openai/gpt-5.2-pro\",\n name: \"GPT-5.2 Pro\",\n inputPrice: 21.0,\n outputPrice: 168.0,\n contextWindow: 400000,\n maxOutput: 128000,\n reasoning: true,\n },\n\n // OpenAI GPT-4 Family\n {\n id: \"openai/gpt-4.1\",\n name: \"GPT-4.1\",\n inputPrice: 2.0,\n outputPrice: 8.0,\n contextWindow: 128000,\n maxOutput: 16384,\n vision: true,\n },\n {\n id: \"openai/gpt-4.1-mini\",\n name: \"GPT-4.1 Mini\",\n inputPrice: 0.4,\n outputPrice: 1.6,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"openai/gpt-4.1-nano\",\n name: \"GPT-4.1 Nano\",\n inputPrice: 0.1,\n outputPrice: 0.4,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"openai/gpt-4o\",\n name: \"GPT-4o\",\n inputPrice: 2.5,\n outputPrice: 10.0,\n contextWindow: 128000,\n maxOutput: 16384,\n vision: true,\n agentic: true,\n },\n {\n id: \"openai/gpt-4o-mini\",\n name: \"GPT-4o Mini\",\n inputPrice: 0.15,\n outputPrice: 0.6,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n\n // OpenAI O-series (Reasoning)\n {\n id: \"openai/o1\",\n name: \"o1\",\n inputPrice: 15.0,\n outputPrice: 60.0,\n contextWindow: 200000,\n maxOutput: 100000,\n reasoning: true,\n },\n {\n id: \"openai/o1-mini\",\n name: \"o1-mini\",\n inputPrice: 1.1,\n outputPrice: 4.4,\n contextWindow: 128000,\n maxOutput: 65536,\n reasoning: true,\n },\n {\n id: \"openai/o3\",\n name: \"o3\",\n inputPrice: 2.0,\n outputPrice: 8.0,\n contextWindow: 200000,\n maxOutput: 100000,\n reasoning: true,\n },\n {\n id: \"openai/o3-mini\",\n name: \"o3-mini\",\n inputPrice: 1.1,\n outputPrice: 4.4,\n contextWindow: 128000,\n maxOutput: 65536,\n reasoning: true,\n },\n {\n id: \"openai/o4-mini\",\n name: \"o4-mini\",\n inputPrice: 1.1,\n outputPrice: 4.4,\n contextWindow: 128000,\n maxOutput: 65536,\n reasoning: true,\n },\n\n // Anthropic - all Claude models excel at agentic workflows\n {\n id: \"anthropic/claude-haiku-4.5\",\n name: \"Claude Haiku 4.5\",\n inputPrice: 1.0,\n outputPrice: 5.0,\n contextWindow: 200000,\n maxOutput: 8192,\n agentic: true,\n },\n {\n id: \"anthropic/claude-sonnet-4\",\n name: \"Claude Sonnet 4\",\n inputPrice: 3.0,\n outputPrice: 15.0,\n contextWindow: 200000,\n maxOutput: 64000,\n reasoning: true,\n agentic: true,\n },\n {\n id: \"anthropic/claude-opus-4\",\n name: \"Claude Opus 4\",\n inputPrice: 15.0,\n outputPrice: 75.0,\n contextWindow: 200000,\n maxOutput: 32000,\n reasoning: true,\n agentic: true,\n },\n {\n id: \"anthropic/claude-opus-4.5\",\n name: \"Claude Opus 4.5\",\n inputPrice: 5.0,\n outputPrice: 25.0,\n contextWindow: 200000,\n maxOutput: 32000,\n reasoning: true,\n agentic: true,\n },\n\n // Google\n {\n id: \"google/gemini-3-pro-preview\",\n name: \"Gemini 3 Pro Preview\",\n inputPrice: 2.0,\n outputPrice: 12.0,\n contextWindow: 1050000,\n maxOutput: 65536,\n reasoning: true,\n vision: true,\n },\n {\n id: \"google/gemini-2.5-pro\",\n name: \"Gemini 2.5 Pro\",\n inputPrice: 1.25,\n outputPrice: 10.0,\n contextWindow: 1050000,\n maxOutput: 65536,\n reasoning: true,\n vision: true,\n },\n {\n id: \"google/gemini-2.5-flash\",\n name: \"Gemini 2.5 Flash\",\n inputPrice: 0.15,\n outputPrice: 0.6,\n contextWindow: 1000000,\n maxOutput: 65536,\n },\n\n // DeepSeek\n {\n id: \"deepseek/deepseek-chat\",\n name: \"DeepSeek V3.2 Chat\",\n inputPrice: 0.28,\n outputPrice: 0.42,\n contextWindow: 128000,\n maxOutput: 8192,\n },\n {\n id: \"deepseek/deepseek-reasoner\",\n name: \"DeepSeek V3.2 Reasoner\",\n inputPrice: 0.28,\n outputPrice: 0.42,\n contextWindow: 128000,\n maxOutput: 8192,\n reasoning: true,\n },\n\n // Moonshot / Kimi - optimized for agentic workflows\n {\n id: \"moonshot/kimi-k2.5\",\n name: \"Kimi K2.5\",\n inputPrice: 0.5,\n outputPrice: 2.4,\n contextWindow: 262144,\n maxOutput: 8192,\n reasoning: true,\n vision: true,\n agentic: true,\n },\n\n // xAI / Grok\n {\n id: \"xai/grok-3\",\n name: \"Grok 3\",\n inputPrice: 3.0,\n outputPrice: 15.0,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-3-fast\",\n name: \"Grok 3 Fast\",\n inputPrice: 5.0,\n outputPrice: 25.0,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-3-mini\",\n name: \"Grok 3 Mini\",\n inputPrice: 0.3,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n },\n\n // xAI Grok 4 Family - Ultra-cheap fast models\n {\n id: \"xai/grok-4-fast-reasoning\",\n name: \"Grok 4 Fast Reasoning\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-4-fast-non-reasoning\",\n name: \"Grok 4 Fast\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n },\n {\n id: \"xai/grok-4-1-fast-reasoning\",\n name: \"Grok 4.1 Fast Reasoning\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-4-1-fast-non-reasoning\",\n name: \"Grok 4.1 Fast\",\n inputPrice: 0.2,\n outputPrice: 0.5,\n contextWindow: 131072,\n maxOutput: 16384,\n },\n {\n id: \"xai/grok-code-fast-1\",\n name: \"Grok Code Fast\",\n inputPrice: 0.2,\n outputPrice: 1.5,\n contextWindow: 131072,\n maxOutput: 16384,\n agentic: true, // Good for coding tasks\n },\n {\n id: \"xai/grok-4-0709\",\n name: \"Grok 4 (0709)\",\n inputPrice: 3.0,\n outputPrice: 15.0,\n contextWindow: 131072,\n maxOutput: 16384,\n reasoning: true,\n },\n {\n id: \"xai/grok-2-vision\",\n name: \"Grok 2 Vision\",\n inputPrice: 2.0,\n outputPrice: 10.0,\n contextWindow: 131072,\n maxOutput: 16384,\n vision: true,\n },\n\n // NVIDIA - Free/cheap models\n {\n id: \"nvidia/gpt-oss-120b\",\n name: \"NVIDIA GPT-OSS 120B\",\n inputPrice: 0,\n outputPrice: 0,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"nvidia/gpt-oss-20b\",\n name: \"NVIDIA GPT-OSS 20B\",\n inputPrice: 0,\n outputPrice: 0,\n contextWindow: 128000,\n maxOutput: 16384,\n },\n {\n id: \"nvidia/kimi-k2.5\",\n name: \"NVIDIA Kimi K2.5\",\n inputPrice: 0.001,\n outputPrice: 0.001,\n contextWindow: 262144,\n maxOutput: 16384,\n },\n];\n\n/**\n * Convert BlockRun model definitions to OpenClaw ModelDefinitionConfig format.\n */\nfunction toOpenClawModel(m: BlockRunModel): ModelDefinitionConfig {\n return {\n id: m.id,\n name: m.name,\n api: \"openai-completions\",\n reasoning: m.reasoning ?? false,\n input: m.vision ? [\"text\", \"image\"] : [\"text\"],\n cost: {\n input: m.inputPrice,\n output: m.outputPrice,\n cacheRead: 0,\n cacheWrite: 0,\n },\n contextWindow: m.contextWindow,\n maxTokens: m.maxOutput,\n };\n}\n\n/**\n * Alias models that map to real models.\n * These allow users to use friendly names like \"free\" or \"gpt-120b\".\n */\nconst ALIAS_MODELS: ModelDefinitionConfig[] = Object.entries(MODEL_ALIASES)\n .map(([alias, targetId]) => {\n const target = BLOCKRUN_MODELS.find((m) => m.id === targetId);\n if (!target) return null;\n return toOpenClawModel({ ...target, id: alias, name: `${alias} → ${target.name}` });\n })\n .filter((m): m is ModelDefinitionConfig => m !== null);\n\n/**\n * All BlockRun models in OpenClaw format (including aliases).\n */\nexport const OPENCLAW_MODELS: ModelDefinitionConfig[] = [\n ...BLOCKRUN_MODELS.map(toOpenClawModel),\n ...ALIAS_MODELS,\n];\n\n/**\n * Build a ModelProviderConfig for BlockRun.\n *\n * @param baseUrl - The proxy's local base URL (e.g., \"http://127.0.0.1:12345\")\n */\nexport function buildProviderModels(baseUrl: string): ModelProviderConfig {\n return {\n baseUrl: `${baseUrl}/v1`,\n api: \"openai-completions\",\n models: OPENCLAW_MODELS,\n };\n}\n\n/**\n * Check if a model is optimized for agentic workflows.\n * Agentic models continue autonomously with multi-step tasks\n * instead of stopping and waiting for user input.\n */\nexport function isAgenticModel(modelId: string): boolean {\n const model = BLOCKRUN_MODELS.find(\n (m) => m.id === modelId || m.id === modelId.replace(\"blockrun/\", \"\"),\n );\n return model?.agentic ?? false;\n}\n\n/**\n * Get all agentic-capable models.\n */\nexport function getAgenticModels(): string[] {\n return BLOCKRUN_MODELS.filter((m) => m.agentic).map((m) => m.id);\n}\n\n/**\n * Get context window size for a model.\n * Returns undefined if model not found.\n */\nexport function getModelContextWindow(modelId: string): number | undefined {\n const normalized = modelId.replace(\"blockrun/\", \"\");\n const model = BLOCKRUN_MODELS.find((m) => m.id === normalized);\n return model?.contextWindow;\n}\n\n/**\n * Check if a model has reasoning/thinking capabilities.\n * Reasoning models may require reasoning_content in assistant tool_call messages.\n */\nexport function isReasoningModel(modelId: string): boolean {\n const normalized = modelId.replace(\"blockrun/\", \"\");\n const model = BLOCKRUN_MODELS.find((m) => m.id === normalized);\n return model?.reasoning ?? false;\n}\n","/**\n * Usage Logger\n *\n * Logs every LLM request as a JSON line to a daily log file.\n * Files: ~/.openclaw/blockrun/logs/usage-YYYY-MM-DD.jsonl\n *\n * MVP: append-only JSON lines. No rotation, no cleanup.\n * Logging never breaks the request flow — all errors are swallowed.\n */\n\nimport { appendFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport type UsageEntry = {\n timestamp: string;\n model: string;\n tier: string;\n cost: number;\n baselineCost: number;\n savings: number; // 0-1 percentage\n latencyMs: number;\n};\n\nconst LOG_DIR = join(homedir(), \".openclaw\", \"blockrun\", \"logs\");\nlet dirReady = false;\n\nasync function ensureDir(): Promise<void> {\n if (dirReady) return;\n await mkdir(LOG_DIR, { recursive: true });\n dirReady = true;\n}\n\n/**\n * Log a usage entry as a JSON line.\n */\nexport async function logUsage(entry: UsageEntry): Promise<void> {\n try {\n await ensureDir();\n const date = entry.timestamp.slice(0, 10); // YYYY-MM-DD\n const file = join(LOG_DIR, `usage-${date}.jsonl`);\n await appendFile(file, JSON.stringify(entry) + \"\\n\");\n } catch {\n // Never break the request flow\n }\n}\n","/**\n * Usage Statistics Aggregator\n *\n * Reads usage log files and aggregates statistics for terminal display.\n * Supports filtering by date range and provides multiple aggregation views.\n */\n\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { UsageEntry } from \"./logger.js\";\n\nconst LOG_DIR = join(homedir(), \".openclaw\", \"blockrun\", \"logs\");\n\nexport type DailyStats = {\n date: string;\n totalRequests: number;\n totalCost: number;\n totalBaselineCost: number;\n totalSavings: number;\n avgLatencyMs: number;\n byTier: Record<string, { count: number; cost: number }>;\n byModel: Record<string, { count: number; cost: number }>;\n};\n\nexport type AggregatedStats = {\n period: string;\n totalRequests: number;\n totalCost: number;\n totalBaselineCost: number;\n totalSavings: number;\n savingsPercentage: number;\n avgLatencyMs: number;\n avgCostPerRequest: number;\n byTier: Record<string, { count: number; cost: number; percentage: number }>;\n byModel: Record<string, { count: number; cost: number; percentage: number }>;\n dailyBreakdown: DailyStats[];\n entriesWithBaseline: number; // Entries with valid baseline tracking\n};\n\n/**\n * Parse a JSONL log file into usage entries.\n * Handles both old format (without tier/baselineCost) and new format.\n */\nasync function parseLogFile(filePath: string): Promise<UsageEntry[]> {\n try {\n const content = await readFile(filePath, \"utf-8\");\n const lines = content.trim().split(\"\\n\").filter(Boolean);\n return lines.map((line) => {\n const entry = JSON.parse(line) as Partial<UsageEntry>;\n // Handle old format entries\n return {\n timestamp: entry.timestamp || new Date().toISOString(),\n model: entry.model || \"unknown\",\n tier: entry.tier || \"UNKNOWN\",\n cost: entry.cost || 0,\n baselineCost: entry.baselineCost || entry.cost || 0,\n savings: entry.savings || 0,\n latencyMs: entry.latencyMs || 0,\n };\n });\n } catch {\n return [];\n }\n}\n\n/**\n * Get list of available log files sorted by date (newest first).\n */\nasync function getLogFiles(): Promise<string[]> {\n try {\n const files = await readdir(LOG_DIR);\n return files\n .filter((f) => f.startsWith(\"usage-\") && f.endsWith(\".jsonl\"))\n .sort()\n .reverse();\n } catch {\n return [];\n }\n}\n\n/**\n * Aggregate stats for a single day.\n */\nfunction aggregateDay(date: string, entries: UsageEntry[]): DailyStats {\n const byTier: Record<string, { count: number; cost: number }> = {};\n const byModel: Record<string, { count: number; cost: number }> = {};\n let totalLatency = 0;\n\n for (const entry of entries) {\n // By tier\n if (!byTier[entry.tier]) byTier[entry.tier] = { count: 0, cost: 0 };\n byTier[entry.tier].count++;\n byTier[entry.tier].cost += entry.cost;\n\n // By model\n if (!byModel[entry.model]) byModel[entry.model] = { count: 0, cost: 0 };\n byModel[entry.model].count++;\n byModel[entry.model].cost += entry.cost;\n\n totalLatency += entry.latencyMs;\n }\n\n const totalCost = entries.reduce((sum, e) => sum + e.cost, 0);\n const totalBaselineCost = entries.reduce((sum, e) => sum + e.baselineCost, 0);\n\n return {\n date,\n totalRequests: entries.length,\n totalCost,\n totalBaselineCost,\n totalSavings: totalBaselineCost - totalCost,\n avgLatencyMs: entries.length > 0 ? totalLatency / entries.length : 0,\n byTier,\n byModel,\n };\n}\n\n/**\n * Get aggregated statistics for the last N days.\n */\nexport async function getStats(days: number = 7): Promise<AggregatedStats> {\n const logFiles = await getLogFiles();\n const filesToRead = logFiles.slice(0, days);\n\n const dailyBreakdown: DailyStats[] = [];\n const allByTier: Record<string, { count: number; cost: number }> = {};\n const allByModel: Record<string, { count: number; cost: number }> = {};\n let totalRequests = 0;\n let totalCost = 0;\n let totalBaselineCost = 0;\n let totalLatency = 0;\n\n for (const file of filesToRead) {\n const date = file.replace(\"usage-\", \"\").replace(\".jsonl\", \"\");\n const filePath = join(LOG_DIR, file);\n const entries = await parseLogFile(filePath);\n\n if (entries.length === 0) continue;\n\n const dayStats = aggregateDay(date, entries);\n dailyBreakdown.push(dayStats);\n\n totalRequests += dayStats.totalRequests;\n totalCost += dayStats.totalCost;\n totalBaselineCost += dayStats.totalBaselineCost;\n totalLatency += dayStats.avgLatencyMs * dayStats.totalRequests;\n\n // Merge tier stats\n for (const [tier, stats] of Object.entries(dayStats.byTier)) {\n if (!allByTier[tier]) allByTier[tier] = { count: 0, cost: 0 };\n allByTier[tier].count += stats.count;\n allByTier[tier].cost += stats.cost;\n }\n\n // Merge model stats\n for (const [model, stats] of Object.entries(dayStats.byModel)) {\n if (!allByModel[model]) allByModel[model] = { count: 0, cost: 0 };\n allByModel[model].count += stats.count;\n allByModel[model].cost += stats.cost;\n }\n }\n\n // Calculate percentages\n const byTierWithPercentage: Record<string, { count: number; cost: number; percentage: number }> =\n {};\n for (const [tier, stats] of Object.entries(allByTier)) {\n byTierWithPercentage[tier] = {\n ...stats,\n percentage: totalRequests > 0 ? (stats.count / totalRequests) * 100 : 0,\n };\n }\n\n const byModelWithPercentage: Record<string, { count: number; cost: number; percentage: number }> =\n {};\n for (const [model, stats] of Object.entries(allByModel)) {\n byModelWithPercentage[model] = {\n ...stats,\n percentage: totalRequests > 0 ? (stats.count / totalRequests) * 100 : 0,\n };\n }\n\n const totalSavings = totalBaselineCost - totalCost;\n const savingsPercentage = totalBaselineCost > 0 ? (totalSavings / totalBaselineCost) * 100 : 0;\n\n // Count entries with valid baseline tracking (baseline != cost means tracking was active)\n let entriesWithBaseline = 0;\n for (const day of dailyBreakdown) {\n if (day.totalBaselineCost !== day.totalCost) {\n entriesWithBaseline += day.totalRequests;\n }\n }\n\n return {\n period: days === 1 ? \"today\" : `last ${days} days`,\n totalRequests,\n totalCost,\n totalBaselineCost,\n totalSavings,\n savingsPercentage,\n avgLatencyMs: totalRequests > 0 ? totalLatency / totalRequests : 0,\n avgCostPerRequest: totalRequests > 0 ? totalCost / totalRequests : 0,\n byTier: byTierWithPercentage,\n byModel: byModelWithPercentage,\n dailyBreakdown: dailyBreakdown.reverse(), // Oldest first for charts\n entriesWithBaseline, // How many entries have valid baseline tracking\n };\n}\n\n/**\n * Format stats as ASCII table for terminal display.\n */\nexport function formatStatsAscii(stats: AggregatedStats): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"╔════════════════════════════════════════════════════════════╗\");\n lines.push(\"║ ClawRouter Usage Statistics ║\");\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n\n // Summary\n lines.push(`║ Period: ${stats.period.padEnd(49)}║`);\n lines.push(`║ Total Requests: ${stats.totalRequests.toString().padEnd(41)}║`);\n lines.push(`║ Total Cost: $${stats.totalCost.toFixed(4).padEnd(43)}║`);\n lines.push(`║ Baseline Cost (Opus): $${stats.totalBaselineCost.toFixed(4).padEnd(33)}║`);\n\n // Show savings with note if some entries lack baseline tracking\n const savingsLine = `║ 💰 Total Saved: $${stats.totalSavings.toFixed(4)} (${stats.savingsPercentage.toFixed(1)}%)`;\n if (stats.entriesWithBaseline < stats.totalRequests && stats.entriesWithBaseline > 0) {\n lines.push(savingsLine.padEnd(61) + \"║\");\n const note = `║ (based on ${stats.entriesWithBaseline}/${stats.totalRequests} tracked requests)`;\n lines.push(note.padEnd(61) + \"║\");\n } else {\n lines.push(savingsLine.padEnd(61) + \"║\");\n }\n lines.push(`║ Avg Latency: ${stats.avgLatencyMs.toFixed(0)}ms`.padEnd(61) + \"║\");\n\n // Tier breakdown\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n lines.push(\"║ Routing by Tier: ║\");\n\n // Show all tiers found in data, ordered by known tiers first then others\n const knownTiers = [\"SIMPLE\", \"MEDIUM\", \"COMPLEX\", \"REASONING\"];\n const allTiers = Object.keys(stats.byTier);\n const otherTiers = allTiers.filter((t) => !knownTiers.includes(t));\n const tierOrder = [...knownTiers.filter((t) => stats.byTier[t]), ...otherTiers];\n\n for (const tier of tierOrder) {\n const data = stats.byTier[tier];\n if (data) {\n const bar = \"█\".repeat(Math.min(20, Math.round(data.percentage / 5)));\n const displayTier = tier === \"UNKNOWN\" ? \"OTHER\" : tier;\n const line = `║ ${displayTier.padEnd(10)} ${bar.padEnd(20)} ${data.percentage.toFixed(1).padStart(5)}% (${data.count})`;\n lines.push(line.padEnd(61) + \"║\");\n }\n }\n\n // Top models\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n lines.push(\"║ Top Models: ║\");\n\n const sortedModels = Object.entries(stats.byModel)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, 5);\n\n for (const [model, data] of sortedModels) {\n const shortModel = model.length > 25 ? model.slice(0, 22) + \"...\" : model;\n const line = `║ ${shortModel.padEnd(25)} ${data.count.toString().padStart(5)} reqs $${data.cost.toFixed(4)}`;\n lines.push(line.padEnd(61) + \"║\");\n }\n\n // Daily breakdown (last 7 days)\n if (stats.dailyBreakdown.length > 0) {\n lines.push(\"╠════════════════════════════════════════════════════════════╣\");\n lines.push(\"║ Daily Breakdown: ║\");\n lines.push(\"║ Date Requests Cost Saved ║\");\n\n for (const day of stats.dailyBreakdown.slice(-7)) {\n const saved = day.totalBaselineCost - day.totalCost;\n const line = `║ ${day.date} ${day.totalRequests.toString().padStart(6)} $${day.totalCost.toFixed(4).padStart(8)} $${saved.toFixed(4)}`;\n lines.push(line.padEnd(61) + \"║\");\n }\n }\n\n lines.push(\"╚════════════════════════════════════════════════════════════╝\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * Request Deduplication\n *\n * Prevents double-charging when OpenClaw retries a request after timeout.\n * Tracks in-flight requests and caches completed responses for a short TTL.\n */\n\nimport { createHash } from \"node:crypto\";\n\nexport type CachedResponse = {\n status: number;\n headers: Record<string, string>;\n body: Buffer;\n completedAt: number;\n};\n\ntype InflightEntry = {\n resolve: (result: CachedResponse) => void;\n waiters: Promise<CachedResponse>[];\n};\n\nconst DEFAULT_TTL_MS = 30_000; // 30 seconds\nconst MAX_BODY_SIZE = 1_048_576; // 1MB\n\n/**\n * Canonicalize JSON by sorting object keys recursively.\n * Ensures identical logical content produces identical string regardless of field order.\n */\nfunction canonicalize(obj: unknown): unknown {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(canonicalize);\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = canonicalize((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n}\n\n/**\n * Strip OpenClaw-injected timestamps from message content.\n * Format: [DAY YYYY-MM-DD HH:MM TZ] at the start of messages.\n * Example: [SUN 2026-02-07 13:30 PST] Hello world\n *\n * This ensures requests with different timestamps but same content hash identically.\n */\nconst TIMESTAMP_PATTERN = /^\\[\\w{3}\\s+\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}\\s+\\w+\\]\\s*/;\n\nfunction stripTimestamps(obj: unknown): unknown {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(stripTimestamps);\n }\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (key === \"content\" && typeof value === \"string\") {\n // Strip timestamp prefix from message content\n result[key] = value.replace(TIMESTAMP_PATTERN, \"\");\n } else {\n result[key] = stripTimestamps(value);\n }\n }\n return result;\n}\n\nexport class RequestDeduplicator {\n private inflight = new Map<string, InflightEntry>();\n private completed = new Map<string, CachedResponse>();\n private ttlMs: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS) {\n this.ttlMs = ttlMs;\n }\n\n /** Hash request body to create a dedup key. */\n static hash(body: Buffer): string {\n // Canonicalize JSON to ensure consistent hashing regardless of field order.\n // Also strip OpenClaw-injected timestamps so retries with different timestamps\n // still match the same dedup key.\n let content = body;\n try {\n const parsed = JSON.parse(body.toString());\n const stripped = stripTimestamps(parsed);\n const canonical = canonicalize(stripped);\n content = Buffer.from(JSON.stringify(canonical));\n } catch {\n // Not valid JSON, use raw bytes\n }\n return createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n }\n\n /** Check if a response is cached for this key. */\n getCached(key: string): CachedResponse | undefined {\n const entry = this.completed.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.completedAt > this.ttlMs) {\n this.completed.delete(key);\n return undefined;\n }\n return entry;\n }\n\n /** Check if a request with this key is currently in-flight. Returns a promise to wait on. */\n getInflight(key: string): Promise<CachedResponse> | undefined {\n const entry = this.inflight.get(key);\n if (!entry) return undefined;\n const promise = new Promise<CachedResponse>((resolve) => {\n // Will be resolved when the original request completes\n entry.waiters.push(\n new Promise<CachedResponse>((r) => {\n const orig = entry.resolve;\n entry.resolve = (result) => {\n orig(result);\n resolve(result);\n r(result);\n };\n }),\n );\n });\n return promise;\n }\n\n /** Mark a request as in-flight. */\n markInflight(key: string): void {\n this.inflight.set(key, {\n resolve: () => {},\n waiters: [],\n });\n }\n\n /** Complete an in-flight request — cache result and notify waiters. */\n complete(key: string, result: CachedResponse): void {\n // Only cache responses within size limit\n if (result.body.length <= MAX_BODY_SIZE) {\n this.completed.set(key, result);\n }\n\n const entry = this.inflight.get(key);\n if (entry) {\n entry.resolve(result);\n this.inflight.delete(key);\n }\n\n this.prune();\n }\n\n /** Remove an in-flight entry on error (don't cache failures). */\n removeInflight(key: string): void {\n this.inflight.delete(key);\n }\n\n /** Prune expired completed entries. */\n private prune(): void {\n const now = Date.now();\n for (const [key, entry] of this.completed) {\n if (now - entry.completedAt > this.ttlMs) {\n this.completed.delete(key);\n }\n }\n }\n}\n","/**\n * Balance Monitor for ClawRouter\n *\n * Monitors USDC balance on Base network with intelligent caching.\n * Provides pre-request balance checks to prevent failed payments.\n *\n * Caching Strategy:\n * - TTL: 30 seconds (balance is cached to avoid excessive RPC calls)\n * - Optimistic deduction: after successful payment, subtract estimated cost from cache\n * - Invalidation: on payment failure, immediately refresh from RPC\n */\n\nimport { createPublicClient, http, erc20Abi } from \"viem\";\nimport { base } from \"viem/chains\";\nimport { RpcError } from \"./errors.js\";\n\n/** USDC contract address on Base mainnet */\nconst USDC_BASE = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\" as const;\n\n/** Cache TTL in milliseconds (30 seconds) */\nconst CACHE_TTL_MS = 30_000;\n\n/** Balance thresholds in USDC smallest unit (6 decimals) */\nexport const BALANCE_THRESHOLDS = {\n /** Low balance warning threshold: $1.00 */\n LOW_BALANCE_MICROS: 1_000_000n,\n /** Effectively zero threshold: $0.0001 (covers dust/rounding) */\n ZERO_THRESHOLD: 100n,\n} as const;\n\n/** Balance information returned by checkBalance() */\nexport type BalanceInfo = {\n /** Raw balance in USDC smallest unit (6 decimals) */\n balance: bigint;\n /** Formatted balance as \"$X.XX\" */\n balanceUSD: string;\n /** True if balance < $1.00 */\n isLow: boolean;\n /** True if balance < $0.0001 (effectively zero) */\n isEmpty: boolean;\n /** Wallet address for funding instructions */\n walletAddress: string;\n};\n\n/** Result from checkSufficient() */\nexport type SufficiencyResult = {\n /** True if balance >= estimated cost */\n sufficient: boolean;\n /** Current balance info */\n info: BalanceInfo;\n /** If insufficient, the shortfall as \"$X.XX\" */\n shortfall?: string;\n};\n\n/**\n * Monitors USDC balance on Base network.\n *\n * Usage:\n * const monitor = new BalanceMonitor(\"0x...\");\n * const info = await monitor.checkBalance();\n * if (info.isLow) console.warn(\"Low balance!\");\n */\nexport class BalanceMonitor {\n private readonly client;\n private readonly walletAddress: `0x${string}`;\n\n /** Cached balance (null = not yet fetched) */\n private cachedBalance: bigint | null = null;\n /** Timestamp when cache was last updated */\n private cachedAt = 0;\n\n constructor(walletAddress: string) {\n this.walletAddress = walletAddress as `0x${string}`;\n this.client = createPublicClient({\n chain: base,\n transport: http(undefined, {\n timeout: 10_000, // 10 second timeout to prevent hanging on slow RPC\n }),\n });\n }\n\n /**\n * Check current USDC balance.\n * Uses cache if valid, otherwise fetches from RPC.\n */\n async checkBalance(): Promise<BalanceInfo> {\n const now = Date.now();\n\n // Use cache if valid\n if (this.cachedBalance !== null && now - this.cachedAt < CACHE_TTL_MS) {\n return this.buildInfo(this.cachedBalance);\n }\n\n // Fetch from RPC\n const balance = await this.fetchBalance();\n this.cachedBalance = balance;\n this.cachedAt = now;\n\n return this.buildInfo(balance);\n }\n\n /**\n * Check if balance is sufficient for an estimated cost.\n *\n * @param estimatedCostMicros - Estimated cost in USDC smallest unit (6 decimals)\n */\n async checkSufficient(estimatedCostMicros: bigint): Promise<SufficiencyResult> {\n const info = await this.checkBalance();\n\n if (info.balance >= estimatedCostMicros) {\n return { sufficient: true, info };\n }\n\n const shortfall = estimatedCostMicros - info.balance;\n return {\n sufficient: false,\n info,\n shortfall: this.formatUSDC(shortfall),\n };\n }\n\n /**\n * Optimistically deduct estimated cost from cached balance.\n * Call this after a successful payment to keep cache accurate.\n *\n * @param amountMicros - Amount to deduct in USDC smallest unit\n */\n deductEstimated(amountMicros: bigint): void {\n if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {\n this.cachedBalance -= amountMicros;\n }\n }\n\n /**\n * Invalidate cache, forcing next checkBalance() to fetch from RPC.\n * Call this after a payment failure to get accurate balance.\n */\n invalidate(): void {\n this.cachedBalance = null;\n this.cachedAt = 0;\n }\n\n /**\n * Force refresh balance from RPC (ignores cache).\n */\n async refresh(): Promise<BalanceInfo> {\n this.invalidate();\n return this.checkBalance();\n }\n\n /**\n * Format USDC amount (in micros) as \"$X.XX\".\n */\n formatUSDC(amountMicros: bigint): string {\n // USDC has 6 decimals\n const dollars = Number(amountMicros) / 1_000_000;\n return `$${dollars.toFixed(2)}`;\n }\n\n /**\n * Get the wallet address being monitored.\n */\n getWalletAddress(): string {\n return this.walletAddress;\n }\n\n /** Fetch balance from RPC */\n private async fetchBalance(): Promise<bigint> {\n try {\n const balance = await this.client.readContract({\n address: USDC_BASE,\n abi: erc20Abi,\n functionName: \"balanceOf\",\n args: [this.walletAddress],\n });\n return balance;\n } catch (error) {\n // Throw typed error instead of silently returning 0\n // This allows callers to distinguish \"node down\" from \"wallet empty\"\n throw new RpcError(error instanceof Error ? error.message : \"Unknown error\", error);\n }\n }\n\n /** Build BalanceInfo from raw balance */\n private buildInfo(balance: bigint): BalanceInfo {\n return {\n balance,\n balanceUSD: this.formatUSDC(balance),\n isLow: balance < BALANCE_THRESHOLDS.LOW_BALANCE_MICROS,\n isEmpty: balance < BALANCE_THRESHOLDS.ZERO_THRESHOLD,\n walletAddress: this.walletAddress,\n };\n }\n}\n","/**\n * Typed Error Classes for ClawRouter\n *\n * Provides structured errors for balance-related failures with\n * all necessary information for user-friendly error messages.\n */\n\n/**\n * Thrown when wallet has insufficient USDC balance for a request.\n */\nexport class InsufficientFundsError extends Error {\n readonly code = \"INSUFFICIENT_FUNDS\" as const;\n readonly currentBalanceUSD: string;\n readonly requiredUSD: string;\n readonly walletAddress: string;\n\n constructor(opts: { currentBalanceUSD: string; requiredUSD: string; walletAddress: string }) {\n const msg = [\n `Insufficient balance. Current: ${opts.currentBalanceUSD}, Required: ${opts.requiredUSD}`,\n `Options:`,\n ` 1. Fund wallet: ${opts.walletAddress}`,\n ` 2. Use free model: /model free`,\n ].join(\"\\n\");\n super(msg);\n this.name = \"InsufficientFundsError\";\n this.currentBalanceUSD = opts.currentBalanceUSD;\n this.requiredUSD = opts.requiredUSD;\n this.walletAddress = opts.walletAddress;\n }\n}\n\n/**\n * Thrown when wallet has no USDC balance (or effectively zero).\n */\nexport class EmptyWalletError extends Error {\n readonly code = \"EMPTY_WALLET\" as const;\n readonly walletAddress: string;\n\n constructor(walletAddress: string) {\n const msg = [\n `No USDC balance.`,\n `Options:`,\n ` 1. Fund wallet: ${walletAddress}`,\n ` 2. Use free model: /model free`,\n ` 3. Uninstall: bash ~/.openclaw/extensions/clawrouter/scripts/uninstall.sh`,\n ].join(\"\\n\");\n super(msg);\n this.name = \"EmptyWalletError\";\n this.walletAddress = walletAddress;\n }\n}\n\n/**\n * Type guard to check if an error is InsufficientFundsError.\n */\nexport function isInsufficientFundsError(error: unknown): error is InsufficientFundsError {\n return error instanceof Error && (error as InsufficientFundsError).code === \"INSUFFICIENT_FUNDS\";\n}\n\n/**\n * Type guard to check if an error is EmptyWalletError.\n */\nexport function isEmptyWalletError(error: unknown): error is EmptyWalletError {\n return error instanceof Error && (error as EmptyWalletError).code === \"EMPTY_WALLET\";\n}\n\n/**\n * Type guard to check if an error is a balance-related error.\n */\nexport function isBalanceError(error: unknown): error is InsufficientFundsError | EmptyWalletError {\n return isInsufficientFundsError(error) || isEmptyWalletError(error);\n}\n\n/**\n * Thrown when RPC call fails (network error, node down, etc).\n * Distinguishes infrastructure failures from actual empty wallets.\n */\nexport class RpcError extends Error {\n readonly code = \"RPC_ERROR\" as const;\n readonly originalError: unknown;\n\n constructor(message: string, originalError?: unknown) {\n super(`RPC error: ${message}. Check network connectivity.`);\n this.name = \"RpcError\";\n this.originalError = originalError;\n }\n}\n\n/**\n * Type guard to check if an error is RpcError.\n */\nexport function isRpcError(error: unknown): error is RpcError {\n return error instanceof Error && (error as RpcError).code === \"RPC_ERROR\";\n}\n","/**\n * Single source of truth for version.\n * Reads from package.json at build time via tsup's define.\n */\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\n\n// Read package.json at runtime\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// In dist/, go up one level to find package.json\nconst require = createRequire(import.meta.url);\nconst pkg = require(join(__dirname, \"..\", \"package.json\")) as { version: string };\n\nexport const VERSION = pkg.version;\nexport const USER_AGENT = `clawrouter/${VERSION}`;\n","/**\n * Session Persistence Store\n *\n * Tracks model selections per session to prevent model switching mid-task.\n * When a session is active, the router will continue using the same model\n * instead of re-routing each request.\n */\n\nexport type SessionEntry = {\n model: string;\n tier: string;\n createdAt: number;\n lastUsedAt: number;\n requestCount: number;\n};\n\nexport type SessionConfig = {\n /** Enable session persistence (default: false) */\n enabled: boolean;\n /** Session timeout in ms (default: 30 minutes) */\n timeoutMs: number;\n /** Header name for session ID (default: X-Session-ID) */\n headerName: string;\n};\n\nexport const DEFAULT_SESSION_CONFIG: SessionConfig = {\n enabled: false,\n timeoutMs: 30 * 60 * 1000, // 30 minutes\n headerName: \"x-session-id\",\n};\n\n/**\n * Session persistence store for maintaining model selections.\n */\nexport class SessionStore {\n private sessions: Map<string, SessionEntry> = new Map();\n private config: SessionConfig;\n private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\n constructor(config: Partial<SessionConfig> = {}) {\n this.config = { ...DEFAULT_SESSION_CONFIG, ...config };\n\n // Start cleanup interval (every 5 minutes)\n if (this.config.enabled) {\n this.cleanupInterval = setInterval(() => this.cleanup(), 5 * 60 * 1000);\n }\n }\n\n /**\n * Get the pinned model for a session, if any.\n */\n getSession(sessionId: string): SessionEntry | undefined {\n if (!this.config.enabled || !sessionId) {\n return undefined;\n }\n\n const entry = this.sessions.get(sessionId);\n if (!entry) {\n return undefined;\n }\n\n // Check if session has expired\n const now = Date.now();\n if (now - entry.lastUsedAt > this.config.timeoutMs) {\n this.sessions.delete(sessionId);\n return undefined;\n }\n\n return entry;\n }\n\n /**\n * Pin a model to a session.\n */\n setSession(sessionId: string, model: string, tier: string): void {\n if (!this.config.enabled || !sessionId) {\n return;\n }\n\n const existing = this.sessions.get(sessionId);\n const now = Date.now();\n\n if (existing) {\n existing.lastUsedAt = now;\n existing.requestCount++;\n // Update model if different (e.g., fallback)\n if (existing.model !== model) {\n existing.model = model;\n existing.tier = tier;\n }\n } else {\n this.sessions.set(sessionId, {\n model,\n tier,\n createdAt: now,\n lastUsedAt: now,\n requestCount: 1,\n });\n }\n }\n\n /**\n * Touch a session to extend its timeout.\n */\n touchSession(sessionId: string): void {\n if (!this.config.enabled || !sessionId) {\n return;\n }\n\n const entry = this.sessions.get(sessionId);\n if (entry) {\n entry.lastUsedAt = Date.now();\n entry.requestCount++;\n }\n }\n\n /**\n * Clear a specific session.\n */\n clearSession(sessionId: string): void {\n this.sessions.delete(sessionId);\n }\n\n /**\n * Clear all sessions.\n */\n clearAll(): void {\n this.sessions.clear();\n }\n\n /**\n * Get session stats for debugging.\n */\n getStats(): { count: number; sessions: Array<{ id: string; model: string; age: number }> } {\n const now = Date.now();\n const sessions = Array.from(this.sessions.entries()).map(([id, entry]) => ({\n id: id.slice(0, 8) + \"...\",\n model: entry.model,\n age: Math.round((now - entry.createdAt) / 1000),\n }));\n return { count: this.sessions.size, sessions };\n }\n\n /**\n * Clean up expired sessions.\n */\n private cleanup(): void {\n const now = Date.now();\n for (const [id, entry] of this.sessions) {\n if (now - entry.lastUsedAt > this.config.timeoutMs) {\n this.sessions.delete(id);\n }\n }\n }\n\n /**\n * Stop the cleanup interval.\n */\n close(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n}\n\n/**\n * Generate a session ID from request headers or create a default.\n */\nexport function getSessionId(\n headers: Record<string, string | string[] | undefined>,\n headerName: string = DEFAULT_SESSION_CONFIG.headerName,\n): string | undefined {\n const value = headers[headerName] || headers[headerName.toLowerCase()];\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (Array.isArray(value) && value.length > 0) {\n return value[0];\n }\n return undefined;\n}\n","/**\n * BlockRun Auth Methods for OpenClaw\n *\n * Provides wallet-based authentication for the BlockRun provider.\n * Operators configure their wallet private key, which is used to\n * sign x402 micropayments for LLM inference.\n *\n * Three methods:\n * 1. Auto-generate — create a new wallet on first run, save to ~/.openclaw/blockrun/wallet.key\n * 2. Environment variable — read from BLOCKRUN_WALLET_KEY\n * 3. Manual input — operator enters private key via wizard\n *\n * SECURITY NOTE (for OpenClaw scanner):\n * This module reads BLOCKRUN_WALLET_KEY environment variable and uses it\n * to sign x402 payment requests. This is INTENTIONAL and REQUIRED behavior:\n * - The wallet key signs USDC payments on Base L2 for each LLM API call\n * - Without the key, ClawRouter cannot authorize payments to BlockRun\n * - The key is NEVER transmitted over the network, only used locally for signing\n * - This is standard x402 payment flow, not credential harvesting\n *\n * @see https://x402.org - x402 payment protocol specification\n * @see https://blockrun.ai/docs - BlockRun API documentation\n * @openclaw-security env-access=BLOCKRUN_WALLET_KEY purpose=x402-payment-signing\n */\n\nimport { writeFile, readFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { generatePrivateKey, privateKeyToAccount } from \"viem/accounts\";\nimport type { ProviderAuthMethod, ProviderAuthContext, ProviderAuthResult } from \"./types.js\";\n\nconst WALLET_DIR = join(homedir(), \".openclaw\", \"blockrun\");\nconst WALLET_FILE = join(WALLET_DIR, \"wallet.key\");\n\n// Export for use by wallet command\nexport { WALLET_FILE };\n\n/**\n * Try to load a previously auto-generated wallet key from disk.\n */\nasync function loadSavedWallet(): Promise<string | undefined> {\n try {\n const key = (await readFile(WALLET_FILE, \"utf-8\")).trim();\n if (key.startsWith(\"0x\") && key.length === 66) return key;\n } catch {\n // File doesn't exist yet\n }\n return undefined;\n}\n\n/**\n * Generate a new wallet, save to disk, return the private key.\n */\nasync function generateAndSaveWallet(): Promise<{ key: string; address: string }> {\n const key = generatePrivateKey();\n const account = privateKeyToAccount(key);\n await mkdir(WALLET_DIR, { recursive: true });\n await writeFile(WALLET_FILE, key + \"\\n\", { mode: 0o600 });\n return { key, address: account.address };\n}\n\n/**\n * Resolve wallet key: load saved → env var → auto-generate.\n * Called by index.ts before the auth wizard runs.\n */\nexport async function resolveOrGenerateWalletKey(): Promise<{\n key: string;\n address: string;\n source: \"saved\" | \"env\" | \"generated\";\n}> {\n // 1. Previously saved wallet\n const saved = await loadSavedWallet();\n if (saved) {\n const account = privateKeyToAccount(saved as `0x${string}`);\n return { key: saved, address: account.address, source: \"saved\" };\n }\n\n // 2. Environment variable\n const envKey = process.env.BLOCKRUN_WALLET_KEY;\n if (typeof envKey === \"string\" && envKey.startsWith(\"0x\") && envKey.length === 66) {\n const account = privateKeyToAccount(envKey as `0x${string}`);\n return { key: envKey, address: account.address, source: \"env\" };\n }\n\n // 3. Auto-generate\n const { key, address } = await generateAndSaveWallet();\n return { key, address, source: \"generated\" };\n}\n\n/**\n * Auth method: operator enters their wallet private key directly.\n */\nexport const walletKeyAuth: ProviderAuthMethod = {\n id: \"wallet-key\",\n label: \"Wallet Private Key\",\n hint: \"Enter your EVM wallet private key (0x...) for x402 payments to BlockRun\",\n kind: \"api_key\",\n run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {\n const key = await ctx.prompter.text({\n message: \"Enter your wallet private key (0x...)\",\n validate: (value: string) => {\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"0x\")) return \"Key must start with 0x\";\n if (trimmed.length !== 66) return \"Key must be 66 characters (0x + 64 hex)\";\n if (!/^0x[0-9a-fA-F]{64}$/.test(trimmed)) return \"Key must be valid hex\";\n return undefined;\n },\n });\n\n if (!key || typeof key !== \"string\") {\n throw new Error(\"Wallet key is required\");\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\n \"Wallet key stored securely in OpenClaw credentials.\",\n \"Your wallet signs x402 USDC payments on Base for each LLM call.\",\n \"Fund your wallet with USDC on Base to start using BlockRun models.\",\n ],\n };\n },\n};\n\n/**\n * Auth method: read wallet key from BLOCKRUN_WALLET_KEY environment variable.\n */\nexport const envKeyAuth: ProviderAuthMethod = {\n id: \"env-key\",\n label: \"Environment Variable\",\n hint: \"Use BLOCKRUN_WALLET_KEY environment variable\",\n kind: \"api_key\",\n run: async (): Promise<ProviderAuthResult> => {\n const key = process.env.BLOCKRUN_WALLET_KEY;\n\n if (!key) {\n throw new Error(\n \"BLOCKRUN_WALLET_KEY environment variable is not set. \" +\n \"Set it to your EVM wallet private key (0x...).\",\n );\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\"Using wallet key from BLOCKRUN_WALLET_KEY environment variable.\"],\n };\n },\n};\n","#!/usr/bin/env node\n/**\n * ClawRouter CLI\n *\n * Standalone proxy for deployed setups where the proxy needs to survive gateway restarts.\n *\n * Usage:\n * npx @blockrun/clawrouter # Start standalone proxy\n * npx @blockrun/clawrouter --version # Show version\n * npx @blockrun/clawrouter --port 8402 # Custom port\n *\n * For production deployments, use with PM2:\n * pm2 start \"npx @blockrun/clawrouter\" --name clawrouter\n */\n\nimport { startProxy, getProxyPort } from \"./proxy.js\";\nimport { resolveOrGenerateWalletKey } from \"./auth.js\";\nimport { BalanceMonitor } from \"./balance.js\";\nimport { VERSION } from \"./version.js\";\n\nfunction printHelp(): void {\n console.log(`\nClawRouter v${VERSION} - Smart LLM Router\n\nUsage:\n clawrouter [options]\n\nOptions:\n --version, -v Show version number\n --help, -h Show this help message\n --port <number> Port to listen on (default: ${getProxyPort()})\n\nExamples:\n # Start standalone proxy (survives gateway restarts)\n npx @blockrun/clawrouter\n\n # Start on custom port\n npx @blockrun/clawrouter --port 9000\n\n # Production deployment with PM2\n pm2 start \"npx @blockrun/clawrouter\" --name clawrouter\n\nEnvironment Variables:\n BLOCKRUN_WALLET_KEY Private key for x402 payments (auto-generated if not set)\n BLOCKRUN_PROXY_PORT Default proxy port (default: 8402)\n\nFor more info: https://github.com/BlockRunAI/ClawRouter\n`);\n}\n\nfunction parseArgs(args: string[]): { version: boolean; help: boolean; port?: number } {\n const result = { version: false, help: false, port: undefined as number | undefined };\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--version\" || arg === \"-v\") {\n result.version = true;\n } else if (arg === \"--help\" || arg === \"-h\") {\n result.help = true;\n } else if (arg === \"--port\" && args[i + 1]) {\n result.port = parseInt(args[i + 1], 10);\n i++; // Skip next arg\n }\n }\n\n return result;\n}\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv.slice(2));\n\n if (args.version) {\n console.log(VERSION);\n process.exit(0);\n }\n\n if (args.help) {\n printHelp();\n process.exit(0);\n }\n\n // Resolve wallet key\n const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();\n\n if (source === \"generated\") {\n console.log(`[ClawRouter] Generated new wallet: ${address}`);\n } else if (source === \"saved\") {\n console.log(`[ClawRouter] Using saved wallet: ${address}`);\n } else {\n console.log(`[ClawRouter] Using wallet from BLOCKRUN_WALLET_KEY: ${address}`);\n }\n\n // Start the proxy\n const proxy = await startProxy({\n walletKey,\n port: args.port,\n onReady: (port) => {\n console.log(`[ClawRouter] Proxy listening on http://127.0.0.1:${port}`);\n console.log(`[ClawRouter] Health check: http://127.0.0.1:${port}/health`);\n },\n onError: (error) => {\n console.error(`[ClawRouter] Error: ${error.message}`);\n },\n onRouted: (decision) => {\n const cost = decision.costEstimate.toFixed(4);\n const saved = (decision.savings * 100).toFixed(0);\n console.log(`[ClawRouter] [${decision.tier}] ${decision.model} $${cost} (saved ${saved}%)`);\n },\n onLowBalance: (info) => {\n console.warn(`[ClawRouter] Low balance: ${info.balanceUSD}. Fund: ${info.walletAddress}`);\n },\n onInsufficientFunds: (info) => {\n console.error(\n `[ClawRouter] Insufficient funds. Balance: ${info.balanceUSD}, Need: ${info.requiredUSD}`,\n );\n },\n });\n\n // Check balance\n const monitor = new BalanceMonitor(address);\n try {\n const balance = await monitor.checkBalance();\n if (balance.isEmpty) {\n console.log(`[ClawRouter] Wallet balance: $0.00 (using FREE model)`);\n console.log(`[ClawRouter] Fund wallet for premium models: ${address}`);\n } else if (balance.isLow) {\n console.log(`[ClawRouter] Wallet balance: ${balance.balanceUSD} (low)`);\n } else {\n console.log(`[ClawRouter] Wallet balance: ${balance.balanceUSD}`);\n }\n } catch {\n console.log(`[ClawRouter] Wallet: ${address} (balance check pending)`);\n }\n\n console.log(`[ClawRouter] Ready - Ctrl+C to stop`);\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\n[ClawRouter] Received ${signal}, shutting down...`);\n try {\n await proxy.close();\n console.log(`[ClawRouter] Proxy closed`);\n process.exit(0);\n } catch (err) {\n console.error(`[ClawRouter] Error during shutdown: ${err}`);\n process.exit(1);\n }\n };\n\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"));\n\n // Keep process alive\n await new Promise(() => {});\n}\n\nmain().catch((err) => {\n console.error(`[ClawRouter] Fatal error: ${err.message}`);\n process.exit(1);\n});\n"],"mappings":"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MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA;AAAA,MAEd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA,IAGA,iBAAiB;AAAA;AAAA,MAEf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,M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","names":["privateKeyToAccount","response","paymentHeader","confidence","join","homedir","LOG_DIR","DEFAULT_TTL_MS","USDC_BASE","join","require","sanitized","account","privateKeyToAccount","balanceMonitor","baseUrl","existingWallet","readFile","mkdir","join","homedir","privateKeyToAccount","readFile","privateKeyToAccount","mkdir"]}
|