droid-patch 0.13.3 → 0.13.4

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.mjs CHANGED
@@ -1258,15 +1258,33 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
1258
1258
  });
1259
1259
  }
1260
1260
  if (reasoningEffort) {
1261
+ patches.push({
1262
+ name: "reasoningEffortSupportedXHighList",
1263
+ description: "Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model list",
1264
+ pattern: Buffer.from(""),
1265
+ replacement: Buffer.from(""),
1266
+ regexPattern: /id:([A-Za-z$_])\.id,displayName:\1\.displayName,shortDisplayName:\1\.(?:displayName|id),modelProvider:\1\.provider\s*,supportedReasoningEfforts:\["(?:none|high)"(?:,"xhigh")?\],defaultReasoningEffort:"(?:none|high)",isCustom:!([01]),noImageSupport:(?:\1\.noImageSupport|!1)/g,
1267
+ regexReplacement: "id:$1.id,displayName:$1.displayName,shortDisplayName:$1.displayName,modelProvider:$1.provider,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$2,noImageSupport:!1",
1268
+ alreadyPatchedRegexPattern: /id:([A-Za-z$_])\.id,displayName:\1\.displayName,shortDisplayName:\1\.displayName,modelProvider:\1\.provider,supportedReasoningEfforts:\["low","high","xhigh"\],defaultReasoningEffort:"high",isCustom:!([01]),noImageSupport:!1/g
1269
+ });
1270
+ patches.push({
1271
+ name: "reasoningEffortSupportedXHighResolver",
1272
+ description: "Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model config resolver",
1273
+ pattern: Buffer.from(""),
1274
+ replacement: Buffer.from(""),
1275
+ regexPattern: /id:([A-Za-z$_])\.model,modelProvider:\1\.provider\s*,displayName:([A-Za-z$_]),shortDisplayName:\2,supportedReasoningEfforts:\["(?:none|high)"(?:,"xhigh")?\],defaultReasoningEffort:"(?:none|high)",isCustom:!([01]),noImageSupport:(?:\1\.noImageSupport|!1)/g,
1276
+ regexReplacement: "id:$1.model,modelProvider:$1.provider,displayName:$2,shortDisplayName:$2,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$3,noImageSupport:!1",
1277
+ alreadyPatchedRegexPattern: /id:([A-Za-z$_])\.model,modelProvider:\1\.provider,displayName:([A-Za-z$_]),shortDisplayName:\2,supportedReasoningEfforts:\["low","high","xhigh"\],defaultReasoningEffort:"high",isCustom:!([01]),noImageSupport:!1/g
1278
+ });
1261
1279
  patches.push({
1262
1280
  name: "reasoningEffortSupported",
1263
- description: "Change supportedReasoningEfforts:[\"none\"] to [\"high\"]",
1281
+ description: "Fallback: Change supportedReasoningEfforts:[\"none\"] to [\"high\"] (for legacy/custom: configs)",
1264
1282
  pattern: Buffer.from("supportedReasoningEfforts:[\"none\"]"),
1265
1283
  replacement: Buffer.from("supportedReasoningEfforts:[\"high\"]")
1266
1284
  });
1267
1285
  patches.push({
1268
1286
  name: "reasoningEffortDefault",
1269
- description: "Change defaultReasoningEffort:\"none\" to \"high\"",
1287
+ description: "Fallback: Change defaultReasoningEffort:\"none\" to \"high\"",
1270
1288
  pattern: Buffer.from("defaultReasoningEffort:\"none\""),
1271
1289
  replacement: Buffer.from("defaultReasoningEffort:\"high\"")
1272
1290
  });
@@ -1287,9 +1305,9 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
1287
1305
  description: "Bypass reasoning effort validation (allows xhigh in settings.json)",
1288
1306
  pattern: Buffer.from(""),
1289
1307
  replacement: Buffer.from(""),
1290
- regexPattern: /([A-Z])!=="none"&&\1!=="off"&&!([A-Z])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g,
1308
+ regexPattern: /([A-Za-z$_])!=="none"&&\1!=="off"&&!([A-Za-z$_])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g,
1291
1309
  regexReplacement: "$1!=\"none\"&&$1!=\"off\"&&0&&$2.$3.includes($1)",
1292
- alreadyPatchedRegexPattern: /([A-Z])!="none"&&\1!="off"&&0&&([A-Z])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g
1310
+ alreadyPatchedRegexPattern: /([A-Za-z$_])!="none"&&\1!="off"&&0&&([A-Za-z$_])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g
1293
1311
  });
1294
1312
  }
1295
1313
  if (noTelemetry) {
@@ -1512,15 +1530,33 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
1512
1530
  });
1513
1531
  }
1514
1532
  if (meta.patches.reasoningEffort) {
1533
+ patches.push({
1534
+ name: "reasoningEffortSupportedXHighList",
1535
+ description: "Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model list",
1536
+ pattern: Buffer.from(""),
1537
+ replacement: Buffer.from(""),
1538
+ regexPattern: /id:([A-Za-z$_])\.id,displayName:\1\.displayName,shortDisplayName:\1\.(?:displayName|id),modelProvider:\1\.provider\s*,supportedReasoningEfforts:\["(?:none|high)"(?:,"xhigh")?\],defaultReasoningEffort:"(?:none|high)",isCustom:!([01]),noImageSupport:(?:\1\.noImageSupport|!1)/g,
1539
+ regexReplacement: "id:$1.id,displayName:$1.displayName,shortDisplayName:$1.displayName,modelProvider:$1.provider,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$2,noImageSupport:!1",
1540
+ alreadyPatchedRegexPattern: /id:([A-Za-z$_])\.id,displayName:\1\.displayName,shortDisplayName:\1\.displayName,modelProvider:\1\.provider,supportedReasoningEfforts:\["low","high","xhigh"\],defaultReasoningEffort:"high",isCustom:!([01]),noImageSupport:!1/g
1541
+ });
1542
+ patches.push({
1543
+ name: "reasoningEffortSupportedXHighResolver",
1544
+ description: "Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model config resolver",
1545
+ pattern: Buffer.from(""),
1546
+ replacement: Buffer.from(""),
1547
+ regexPattern: /id:([A-Za-z$_])\.model,modelProvider:\1\.provider\s*,displayName:([A-Za-z$_]),shortDisplayName:\2,supportedReasoningEfforts:\["(?:none|high)"(?:,"xhigh")?\],defaultReasoningEffort:"(?:none|high)",isCustom:!([01]),noImageSupport:(?:\1\.noImageSupport|!1)/g,
1548
+ regexReplacement: "id:$1.model,modelProvider:$1.provider,displayName:$2,shortDisplayName:$2,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$3,noImageSupport:!1",
1549
+ alreadyPatchedRegexPattern: /id:([A-Za-z$_])\.model,modelProvider:\1\.provider,displayName:([A-Za-z$_]),shortDisplayName:\2,supportedReasoningEfforts:\["low","high","xhigh"\],defaultReasoningEffort:"high",isCustom:!([01]),noImageSupport:!1/g
1550
+ });
1515
1551
  patches.push({
1516
1552
  name: "reasoningEffortSupported",
1517
- description: "Change supportedReasoningEfforts:[\"none\"] to [\"high\"]",
1553
+ description: "Fallback: Change supportedReasoningEfforts:[\"none\"] to [\"high\"] (for legacy/custom: configs)",
1518
1554
  pattern: Buffer.from("supportedReasoningEfforts:[\"none\"]"),
1519
1555
  replacement: Buffer.from("supportedReasoningEfforts:[\"high\"]")
1520
1556
  });
1521
1557
  patches.push({
1522
1558
  name: "reasoningEffortDefault",
1523
- description: "Change defaultReasoningEffort:\"none\" to \"high\"",
1559
+ description: "Fallback: Change defaultReasoningEffort:\"none\" to \"high\"",
1524
1560
  pattern: Buffer.from("defaultReasoningEffort:\"none\""),
1525
1561
  replacement: Buffer.from("defaultReasoningEffort:\"high\"")
1526
1562
  });
@@ -1541,9 +1577,9 @@ bin("droid-patch", "CLI tool to patch droid binary with various modifications").
1541
1577
  description: "Bypass reasoning effort validation (allows xhigh in settings.json)",
1542
1578
  pattern: Buffer.from(""),
1543
1579
  replacement: Buffer.from(""),
1544
- regexPattern: /([A-Z])!=="none"&&\1!=="off"&&!([A-Z])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g,
1580
+ regexPattern: /([A-Za-z$_])!=="none"&&\1!=="off"&&!([A-Za-z$_])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g,
1545
1581
  regexReplacement: "$1!=\"none\"&&$1!=\"off\"&&0&&$2.$3.includes($1)",
1546
- alreadyPatchedRegexPattern: /([A-Z])!="none"&&\1!="off"&&0&&([A-Z])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g
1582
+ alreadyPatchedRegexPattern: /([A-Za-z$_])!="none"&&\1!="off"&&0&&([A-Za-z$_])\.(supportedReasoningEfforts|reasoningEffort\.supported)\.includes\(\1\)/g
1547
1583
  });
1548
1584
  }
1549
1585
  if (meta.patches.noTelemetry) {
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["IS_WINDOWS","newModel: CustomModel","index: number","updatedModels: { oldId: string; newId: string; displayName: string }[]","patches: Patch[]","flag: FilterFlag | undefined","allowedFlags: FilterFlag[]","unlink","metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[]","readlink","symlink","validProviders: Provider[]"],"sources":["../src/websearch-external.ts","../src/websearch-native.ts","../src/websearch-patch.ts","../src/model-manager.ts","../src/cli.ts"],"sourcesContent":["/**\n * WebSearch External Providers Mode (--websearch)\n *\n * Priority: Smithery Exa > Google PSE > Tavily > Serper > Brave > SearXNG > DuckDuckGo\n */\n\nexport function generateSearchProxyServerCode(): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (External Providers Mode)\n// Priority: Smithery Exa > Google PSE > Tavily > Serper > Brave > SearXNG > DuckDuckGo\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = 'https://api.factory.ai';\n\nfunction log() { if (DEBUG) console.error.apply(console, ['[websearch]'].concat(Array.from(arguments))); }\n\n// === External Search Providers ===\n\nasync function searchSmitheryExa(query, numResults) {\n const apiKey = process.env.SMITHERY_API_KEY;\n const profile = process.env.SMITHERY_PROFILE;\n if (!apiKey || !profile) return null;\n try {\n const serverUrl = 'https://server.smithery.ai/exa/mcp?api_key=' + encodeURIComponent(apiKey) + '&profile=' + encodeURIComponent(profile);\n const requestBody = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'web_search_exa', arguments: { query: query, numResults: numResults } } });\n const bodyStr = requestBody.replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + serverUrl + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const response = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 30000 }));\n if (response.result && response.result.content) {\n const textContent = response.result.content.find(function(c) { return c.type === 'text'; });\n if (textContent && textContent.text) {\n const results = JSON.parse(textContent.text);\n if (Array.isArray(results) && results.length > 0) {\n return results.slice(0, numResults).map(function(item) {\n return {\n title: item.title || '', url: item.url || '',\n content: item.text || item.snippet || (item.highlights ? item.highlights.join(' ') : '') || ''\n };\n });\n }\n }\n }\n } catch (e) { log('Smithery failed:', e.message); }\n return null;\n}\n\nasync function searchGooglePSE(query, numResults) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = 'https://www.googleapis.com/customsearch/v1?key=' + apiKey + '&cx=' + cx + '&q=' + encodeURIComponent(query) + '&num=' + Math.min(numResults, 10);\n const data = JSON.parse(execSync('curl -s \"' + url + '\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.error) return null;\n return (data.items || []).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n } catch (e) { log('Google PSE failed:', e.message); }\n return null;\n}\n\nasync function searchTavily(query, numResults) {\n const apiKey = process.env.TAVILY_API_KEY;\n if (!apiKey) return null;\n try {\n const bodyStr = JSON.stringify({\n api_key: apiKey,\n query: query,\n max_results: numResults,\n search_depth: 'basic',\n include_answer: false,\n include_images: false,\n include_raw_content: false\n }).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s \"https://api.tavily.com/search\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data && Array.isArray(data.results) && data.results.length > 0) {\n return data.results.slice(0, numResults).map(function(item) {\n return { title: item.title || '', url: item.url || '', content: item.content || item.snippet || item.raw_content || '' };\n });\n }\n } catch (e) { log('Tavily failed:', e.message); }\n return null;\n}\n\nasync function searchSerper(query, numResults) {\n const apiKey = process.env.SERPER_API_KEY;\n if (!apiKey) return null;\n try {\n const bodyStr = JSON.stringify({ q: query, num: numResults }).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s \"https://google.serper.dev/search\" -H \"X-API-KEY: ' + apiKey + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.organic && data.organic.length > 0) {\n return data.organic.slice(0, numResults).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n }\n } catch (e) { log('Serper failed:', e.message); }\n return null;\n}\n\nasync function searchBrave(query, numResults) {\n const apiKey = process.env.BRAVE_API_KEY;\n if (!apiKey) return null;\n try {\n const url = 'https://api.search.brave.com/res/v1/web/search?q=' + encodeURIComponent(query) + '&count=' + numResults;\n const curlCmd = 'curl -s \"' + url + '\" -H \"Accept: application/json\" -H \"X-Subscription-Token: ' + apiKey + '\"';\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.web && data.web.results && data.web.results.length > 0) {\n return data.web.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.description || '' }; });\n }\n } catch (e) { log('Brave failed:', e.message); }\n return null;\n}\n\nasync function searchSearXNG(query, numResults) {\n const searxngUrl = process.env.SEARXNG_URL;\n if (!searxngUrl) return null;\n try {\n const url = searxngUrl + '/search?q=' + encodeURIComponent(query) + '&format=json&engines=google,bing,duckduckgo';\n const data = JSON.parse(execSync('curl -s \"' + url + '\" -H \"Accept: application/json\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.results && data.results.length > 0) {\n return data.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.content || '' }; });\n }\n } catch (e) { log('SearXNG failed:', e.message); }\n return null;\n}\n\nasync function searchDuckDuckGo(query, numResults) {\n try {\n const apiUrl = 'https://api.duckduckgo.com/?q=' + encodeURIComponent(query) + '&format=json&no_html=1&skip_disambig=1';\n const data = JSON.parse(execSync('curl -s \"' + apiUrl + '\" -H \"User-Agent: Mozilla/5.0\"', { encoding: 'utf-8', timeout: 15000 }));\n const results = [];\n if (data.Abstract && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.Abstract });\n }\n var topics = data.RelatedTopics || [];\n for (var i = 0; i < topics.length && results.length < numResults; i++) {\n var topic = topics[i];\n if (topic.Text && topic.FirstURL) results.push({ title: topic.Text.substring(0, 100), url: topic.FirstURL, content: topic.Text });\n if (topic.Topics) {\n for (var j = 0; j < topic.Topics.length && results.length < numResults; j++) {\n var st = topic.Topics[j];\n if (st.Text && st.FirstURL) results.push({ title: st.Text.substring(0, 100), url: st.FirstURL, content: st.Text });\n }\n }\n }\n return results.length > 0 ? results : null;\n } catch (e) { log('DuckDuckGo failed:', e.message); }\n return null;\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n // Priority: Smithery > Google PSE > Tavily > Serper > Brave > SearXNG > DuckDuckGo\n var results = await searchSmitheryExa(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'smithery-exa' };\n \n results = await searchGooglePSE(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'google-pse' };\n \n results = await searchTavily(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'tavily' };\n \n results = await searchSerper(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'serper' };\n \n results = await searchBrave(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'brave' };\n \n results = await searchSearXNG(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'searxng' };\n \n results = await searchDuckDuckGo(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'duckduckgo' };\n \n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'external-providers' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Proxy other requests\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('External providers proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n\nexport function generateExternalSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n const code = generateSearchProxyServerCode();\n return code.replace(\n \"const FACTORY_API = 'https://api.factory.ai';\",\n `const FACTORY_API = '${factoryApiUrl}';`,\n );\n}\n","/**\n * WebSearch Native Provider Mode (--websearch-proxy)\n *\n * Uses model's native websearch based on ~/.factory/settings.json configuration\n * Requires proxy plugin to handle format conversion:\n * - Anthropic provider: anthropic4droid plugin\n * - OpenAI provider: openai4droid plugin (adds CODEX_INSTRUCTIONS)\n *\n * Supported providers:\n * - Anthropic: web_search_20250305 server tool, results in web_search_tool_result\n * - OpenAI: web_search tool, results in message.content[].annotations[] as url_citation\n */\n\nexport function generateNativeSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (Native Provider Mode)\n// Reads ~/.factory/settings.json for model configuration\n// Requires proxy plugin (anthropic4droid) to handle format conversion\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\nconst os = require('os');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = '${factoryApiUrl}';\n\nfunction log(...args) { if (DEBUG) console.error('[websearch]', ...args); }\n\n// === Settings Configuration ===\n\nlet cachedSettings = null;\nlet settingsLastModified = 0;\n\nfunction getFactorySettings() {\n const settingsPath = path.join(os.homedir(), '.factory', 'settings.json');\n try {\n const stats = fs.statSync(settingsPath);\n if (cachedSettings && stats.mtimeMs === settingsLastModified) return cachedSettings;\n cachedSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n settingsLastModified = stats.mtimeMs;\n return cachedSettings;\n } catch (e) {\n log('Failed to load settings.json:', e.message);\n return null;\n }\n}\n\nfunction getCurrentModelConfig() {\n const settings = getFactorySettings();\n if (!settings) return null;\n \n const currentModelId = settings.sessionDefaultSettings?.model;\n if (!currentModelId) return null;\n \n const customModels = settings.customModels || [];\n const modelConfig = customModels.find(m => m.id === currentModelId);\n \n if (modelConfig) {\n log('Model:', modelConfig.displayName, '| Provider:', modelConfig.provider);\n return modelConfig;\n }\n \n if (!currentModelId.startsWith('custom:')) return null;\n log('Model not found:', currentModelId);\n return null;\n}\n\n// === Native Provider WebSearch ===\n\nasync function searchAnthropicNative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n const requestBody = {\n model: model,\n max_tokens: 4096,\n stream: false,\n system: 'You are a web search assistant. Use the web_search tool to find relevant information and return the results.',\n tools: [{ type: 'web_search_20250305', name: 'web_search', max_uses: 1 }],\n tool_choice: { type: 'tool', name: 'web_search' },\n messages: [{ role: 'user', content: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.' }]\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/v1/messages')) endpoint = endpoint.replace(/\\\\/$/, '') + '/v1/messages';\n \n log('Anthropic search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"anthropic-version: 2023-06-01\" -H \"x-api-key: ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n const results = [];\n for (const block of (response.content || [])) {\n if (block.type === 'web_search_tool_result') {\n for (const result of (block.content || [])) {\n if (result.type === 'web_search_result') {\n results.push({\n title: result.title || '',\n url: result.url || '',\n content: result.snippet || result.page_content || ''\n });\n }\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('Anthropic error:', e.message);\n return null;\n }\n}\n\nasync function searchOpenAINative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n // Note: instructions will be added by openai4droid proxy plugin\n const requestBody = {\n model: model,\n stream: false,\n tools: [{ type: 'web_search' }],\n tool_choice: 'required',\n input: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.'\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/responses')) endpoint = endpoint.replace(/\\\\/$/, '') + '/responses';\n \n log('OpenAI search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"Authorization: Bearer ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n // Extract results from url_citation annotations in message output\n const results = [];\n for (const item of (response.output || [])) {\n if (item.type === 'message' && Array.isArray(item.content)) {\n for (const content of item.content) {\n if (content.type === 'output_text' && Array.isArray(content.annotations)) {\n for (const annotation of content.annotations) {\n if (annotation.type === 'url_citation' && annotation.url) {\n results.push({\n title: annotation.title || '',\n url: annotation.url || '',\n content: annotation.title || ''\n });\n }\n }\n }\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('OpenAI error:', e.message);\n return null;\n }\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n const modelConfig = getCurrentModelConfig();\n if (!modelConfig) {\n log('No custom model configured');\n return { results: [], source: 'none' };\n }\n \n const provider = modelConfig.provider;\n let results = null;\n \n if (provider === 'anthropic') results = await searchAnthropicNative(query, numResults, modelConfig);\n else if (provider === 'openai') results = await searchOpenAINative(query, numResults, modelConfig);\n else log('Unsupported provider:', provider);\n \n if (results && results.length > 0) return { results: results, source: 'native-' + provider };\n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'native-provider' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Standalone mode: mock non-LLM APIs\n if (process.env.STANDALONE_MODE === '1') {\n const pathname = url.pathname;\n const isCoreLLMApi = pathname.startsWith('/api/llm/a/') || pathname.startsWith('/api/llm/o/');\n\n if (!isCoreLLMApi) {\n if (pathname === '/api/sessions/create') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ id: 'local-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10) }));\n return;\n }\n if (pathname === '/api/cli/whoami') {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({}));\n return;\n }\n }\n\n // Simple proxy - no SSE transformation (handled by proxy plugin)\n log('Proxy:', req.method, url.pathname);\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('Native provider proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n","/**\n * WebSearch Patch Generator\n *\n * Two modes:\n * - --websearch: External providers (Smithery, Google PSE, Tavily, Serper, Brave, SearXNG, DuckDuckGo)\n * - --websearch-proxy: Native provider via proxy plugin (reads ~/.factory/settings.json)\n */\n\nimport type { Patch } from \"./patcher.ts\";\nimport { writeFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { platform } from \"node:os\";\nimport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nimport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\nconst IS_WINDOWS = platform() === \"win32\";\n\n// Re-export for backward compatibility\nexport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nexport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\n/**\n * Generate wrapper script for standalone proxy mode\n */\nfunction generateWrapperScript(droidPath: string, proxyScriptPath: string): string {\n return `#!/bin/bash\n# Droid with WebSearch Proxy\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPORT_FILE=\"/tmp/droid-search-proxy-$$.port\"\n\nstart_proxy() {\n SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" &\n PROXY_PID=$!\n for i in {1..20}; do\n if [ -f \"$PORT_FILE\" ]; then\n PORT=$(cat \"$PORT_FILE\")\n if curl -s \"http://127.0.0.1:$PORT/health\" > /dev/null 2>&1; then\n echo \"[websearch] Proxy started on port $PORT\"\n return 0\n fi\n fi\n sleep 0.2\n done\n echo \"[websearch] Failed to start proxy\"\n kill $PROXY_PID 2>/dev/null\n return 1\n}\n\ncleanup() {\n [ -n \"$PROXY_PID\" ] && kill $PROXY_PID 2>/dev/null\n [ -f \"$PORT_FILE\" ] && rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT\n\nif ! start_proxy; then exit 1; fi\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$PORT\"\nexec \"$DROID_BIN\" \"$@\"\n`;\n}\n\n/**\n * Generate WebSearch Patch (legacy binary patch approach)\n */\nexport function generateWebSearchPatch(): Patch | null {\n const originalUrl = \"https://api.factory.ai\";\n const localUrl = \"http://127.0.0.1:23119\";\n\n if (localUrl.length > originalUrl.length) {\n console.error(`[websearch] Local URL too long: ${localUrl.length} > ${originalUrl.length}`);\n return null;\n }\n\n const paddedUrl = localUrl.padEnd(originalUrl.length, \" \");\n return {\n name: \"webSearch\",\n description: `Replace API URL with local proxy (${localUrl})`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n };\n}\n\n/**\n * Create WebSearch proxy files (legacy)\n */\nexport async function createWebSearchProxyFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ proxyScript: string; wrapperScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-search-proxy.js`);\n const wrapperScriptPath = join(outputDir, `${aliasName}-with-search`);\n\n await writeFile(proxyScriptPath, generateSearchProxyServerCode());\n console.log(`[*] Created search proxy: ${proxyScriptPath}`);\n\n await writeFile(wrapperScriptPath, generateWrapperScript(droidPath, proxyScriptPath));\n await chmod(wrapperScriptPath, 0o755);\n console.log(`[*] Created wrapper script: ${wrapperScriptPath}`);\n\n return { proxyScript: proxyScriptPath, wrapperScript: wrapperScriptPath };\n}\n\n/**\n * Get proxy server code (for export)\n */\nexport function getSearchProxyCode(): string {\n return generateSearchProxyServerCode();\n}\n\n/**\n * Generate unified wrapper script (Unix bash)\n */\nfunction generateUnifiedWrapper(\n droidPath: string,\n proxyScriptPath: string,\n standalone: boolean = false,\n): string {\n const standaloneEnv = standalone ? \"STANDALONE_MODE=1 \" : \"\";\n return `#!/bin/bash\n# Droid with WebSearch\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPROXY_PID=\"\"\nPORT_FILE=\"/tmp/droid-websearch-$$.port\"\nSTANDALONE=\"${standalone ? \"1\" : \"0\"}\"\n\nshould_passthrough() {\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then break; fi\n case \"$arg\" in --help|-h|--version|-V) return 0 ;; esac\n done\n local end_opts=0\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then end_opts=1; continue; fi\n if [ \"$end_opts\" -eq 0 ] && [[ \"$arg\" == -* ]]; then continue; fi\n case \"$arg\" in help|version|completion|completions|exec|plugin) return 0 ;; esac\n break\n done\n return 1\n}\n\nif should_passthrough \"$@\"; then exec \"$DROID_BIN\" \"$@\"; fi\n\ncleanup() {\n if [ -n \"$PROXY_PID\" ] && kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Stopping proxy (PID: $PROXY_PID)\" >&2\n kill \"$PROXY_PID\" 2>/dev/null\n wait \"$PROXY_PID\" 2>/dev/null\n fi\n rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT INT TERM\n\n[ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Starting proxy...\" >&2\n[ \"$STANDALONE\" = \"1\" ] && [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Standalone mode enabled\" >&2\n\nif [ -n \"$DROID_SEARCH_DEBUG\" ]; then\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" 2>&1 &\nelse\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" >/dev/null 2>&1 &\nfi\nPROXY_PID=$!\n\nfor i in {1..50}; do\n if ! kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy process died\" >&2\n break\n fi\n if [ -f \"$PORT_FILE\" ]; then\n ACTUAL_PORT=$(cat \"$PORT_FILE\" 2>/dev/null)\n if [ -n \"$ACTUAL_PORT\" ] && curl -s \"http://127.0.0.1:$ACTUAL_PORT/health\" > /dev/null 2>&1; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy ready on port $ACTUAL_PORT (PID: $PROXY_PID)\" >&2\n break\n fi\n fi\n sleep 0.1\ndone\n\nif [ ! -f \"$PORT_FILE\" ] || [ -z \"$(cat \"$PORT_FILE\" 2>/dev/null)\" ]; then\n echo \"[websearch] Failed to start proxy, running without websearch\" >&2\n cleanup\n exec \"$DROID_BIN\" \"$@\"\nfi\n\nACTUAL_PORT=$(cat \"$PORT_FILE\")\nrm -f \"$PORT_FILE\"\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$ACTUAL_PORT\"\n\"$DROID_BIN\" \"$@\"\nDROID_EXIT_CODE=$?\nexit $DROID_EXIT_CODE\n`;\n}\n\n/**\n * Generate Windows wrapper script (.cmd batch file)\n */\nfunction generateWindowsWrapper(\n droidPath: string,\n proxyScriptPath: string,\n standalone: boolean = false,\n): string {\n const standaloneEnv = standalone ? \"set STANDALONE_MODE=1\\r\\n\" : \"\";\n return `@echo off\nsetlocal enabledelayedexpansion\nREM Droid with WebSearch\nREM Auto-generated by droid-patch --websearch\n\nset \"PROXY_SCRIPT=${proxyScriptPath}\"\nset \"DROID_BIN=${droidPath}\"\nset \"PORT_FILE=%TEMP%\\\\droid-websearch-%RANDOM%%RANDOM%.port\"\n\nREM Check for passthrough commands\nset \"PASSTHROUGH=0\"\nfor %%a in (%*) do (\n if \"%%a\"==\"--help\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"-h\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"--version\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"-V\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"help\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"version\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"completion\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"completions\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"exec\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"plugin\" set \"PASSTHROUGH=1\"\n)\nif \"%PASSTHROUGH%\"==\"1\" (\n \"%DROID_BIN%\" %*\n exit /b %ERRORLEVEL%\n)\n\nREM Start proxy server\n${standaloneEnv}set \"SEARCH_PROXY_PORT=0\"\nset \"SEARCH_PROXY_PORT_FILE=%PORT_FILE%\"\n\nif defined DROID_SEARCH_DEBUG (\n echo [websearch] Starting proxy... 1>&2\n start /b node \"%PROXY_SCRIPT%\"\n) else (\n start /b node \"%PROXY_SCRIPT%\" >nul 2>&1\n)\n\nREM Wait for port file\nset \"RETRY=0\"\n:wait_loop\nif %RETRY% GEQ 50 goto :proxy_failed\nset /a RETRY+=1\ntimeout /t 1 /nobreak >nul 2>&1\nif not exist \"%PORT_FILE%\" goto :wait_loop\n\nREM Read port and check health\nset /p ACTUAL_PORT=<\"%PORT_FILE%\"\nif not defined ACTUAL_PORT goto :wait_loop\n\nREM Simple health check using curl or PowerShell\nwhere curl >nul 2>&1\nif %ERRORLEVEL%==0 (\n curl -s \"http://127.0.0.1:%ACTUAL_PORT%/health\" >nul 2>&1\n if %ERRORLEVEL%==0 goto :proxy_ready\n) else (\n powershell -Command \"try { Invoke-WebRequest -Uri 'http://127.0.0.1:%ACTUAL_PORT%/health' -UseBasicParsing -TimeoutSec 2 | Out-Null; exit 0 } catch { exit 1 }\" >nul 2>&1\n if %ERRORLEVEL%==0 goto :proxy_ready\n)\ngoto :wait_loop\n\n:proxy_failed\necho [websearch] Failed to start proxy, running without websearch 1>&2\ndel \"%PORT_FILE%\" 2>nul\n\"%DROID_BIN%\" %*\nexit /b %ERRORLEVEL%\n\n:proxy_ready\nif defined DROID_SEARCH_DEBUG echo [websearch] Proxy ready on port %ACTUAL_PORT% 1>&2\ndel \"%PORT_FILE%\" 2>nul\n\nset \"FACTORY_API_BASE_URL_OVERRIDE=http://127.0.0.1:%ACTUAL_PORT%\"\n\"%DROID_BIN%\" %*\nset \"DROID_EXIT_CODE=%ERRORLEVEL%\"\n\nREM Cleanup: kill node processes started by this script\nREM Note: Windows doesn't have easy process tree tracking, proxy will exit when parent exits\nexit /b %DROID_EXIT_CODE%\n`;\n}\n\n/**\n * Create unified WebSearch files\n *\n * @param outputDir - Directory to write files to\n * @param droidPath - Path to droid binary\n * @param aliasName - Alias name for the wrapper\n * @param apiBase - Custom API base URL for proxy to forward requests to\n * @param standalone - Standalone mode: mock non-LLM Factory APIs\n * @param useNativeProvider - Use native provider websearch (--websearch-proxy mode)\n */\nexport async function createWebSearchUnifiedFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n apiBase?: string,\n standalone: boolean = false,\n useNativeProvider: boolean = false,\n): Promise<{ wrapperScript: string; preloadScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-proxy.js`);\n // Windows uses .cmd extension, Unix has no extension\n const wrapperScriptPath = IS_WINDOWS\n ? join(outputDir, `${aliasName}.cmd`)\n : join(outputDir, aliasName);\n\n const factoryApiUrl = apiBase || \"https://api.factory.ai\";\n const proxyCode = useNativeProvider\n ? generateNativeSearchProxyServer(factoryApiUrl)\n : generateExternalSearchProxyServer(factoryApiUrl);\n\n await writeFile(proxyScriptPath, proxyCode);\n console.log(`[*] Created proxy script: ${proxyScriptPath}`);\n console.log(\n `[*] Mode: ${useNativeProvider ? \"native provider (requires proxy plugin)\" : \"external providers\"}`,\n );\n\n // Generate platform-specific wrapper\n const wrapperCode = IS_WINDOWS\n ? generateWindowsWrapper(droidPath, proxyScriptPath, standalone)\n : generateUnifiedWrapper(droidPath, proxyScriptPath, standalone);\n\n await writeFile(wrapperScriptPath, wrapperCode);\n if (!IS_WINDOWS) {\n await chmod(wrapperScriptPath, 0o755);\n }\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n if (standalone) {\n console.log(`[*] Standalone mode enabled`);\n }\n\n return {\n wrapperScript: wrapperScriptPath,\n preloadScript: proxyScriptPath,\n };\n}\n\n// === Legacy Preload Mode (kept for compatibility) ===\n\nfunction generatePreloadScript(): string {\n return `// Droid WebSearch Preload Script\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\n\nconst PORT = process.env.DROID_SEARCH_PORT || 23119;\nconst FACTORY_API = 'https://api.factory.ai';\n\nasync function searchGooglePSE(query, num) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = \\`https://www.googleapis.com/customsearch/v1?key=\\${apiKey}&cx=\\${cx}&q=\\${encodeURIComponent(query)}&num=\\${Math.min(num, 10)}\\`;\n const res = await fetch(url);\n const data = await res.json();\n if (data.error) return null;\n return (data.items || []).map(item => ({ title: item.title, url: item.link, content: item.snippet || '' }));\n } catch { return null; }\n}\n\nfunction searchDuckDuckGo(query, num) {\n try {\n const url = \\`https://api.duckduckgo.com/?q=\\${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1\\`;\n const output = execSync(\\`curl -s \"\\${url}\"\\`, { encoding: 'utf8', timeout: 10000 });\n const data = JSON.parse(output);\n const results = [];\n if (data.AbstractText && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.AbstractText });\n }\n for (const t of (data.RelatedTopics || [])) {\n if (results.length >= num) break;\n if (t.Text && t.FirstURL) results.push({ title: t.Text.split(' - ')[0], url: t.FirstURL, content: t.Text });\n if (t.Topics) {\n for (const sub of t.Topics) {\n if (results.length >= num) break;\n if (sub.Text && sub.FirstURL) results.push({ title: sub.Text.split(' - ')[0], url: sub.FirstURL, content: sub.Text });\n }\n }\n }\n return results;\n } catch { return []; }\n}\n\nasync function search(query, num) {\n const googleResults = await searchGooglePSE(query, num);\n if (googleResults?.length > 0) return googleResults;\n return searchDuckDuckGo(query, num);\n}\n\nfunction isPortInUse(port) {\n try { execSync(\\`curl -s http://127.0.0.1:\\${port}/health\\`, { timeout: 1000 }); return true; } catch { return false; }\n}\n\nif (!isPortInUse(PORT)) {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url, \\`http://\\${req.headers.host}\\`);\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', c => body += c);\n req.on('end', async () => {\n try {\n const { query, numResults } = JSON.parse(body);\n const results = await search(query, numResults || 10);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results }));\n } catch (e) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyReq = https.request(proxyUrl, { method: req.method, headers: { ...req.headers, host: proxyUrl.host } }, proxyRes => {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n proxyReq.on('error', () => { res.writeHead(502); res.end(); });\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n });\n server.listen(PORT, '127.0.0.1');\n}\n`;\n}\n\nfunction generateBunfigToml(preloadScriptPath: string): string {\n return `preload = [\"${preloadScriptPath}\"]`;\n}\n\nfunction generatePreloadWrapperScript(droidPath: string, bunfigDir: string): string {\n return `#!/bin/bash\ncd \"${bunfigDir}\"\nexec \"${droidPath}\" --cwd \"$(pwd)\" \"$@\"\n`;\n}\n\nexport async function createWebSearchPreloadFiles(\n droidDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ preloadScript: string; bunfigPath: string; wrapperScript: string }> {\n if (!existsSync(droidDir)) {\n await mkdir(droidDir, { recursive: true });\n }\n\n const preloadScriptPath = join(droidDir, `${aliasName}-search-preload.js`);\n const bunfigPath = join(droidDir, \"bunfig.toml\");\n const wrapperScriptPath = join(droidDir, aliasName);\n\n await writeFile(preloadScriptPath, generatePreloadScript());\n await writeFile(bunfigPath, generateBunfigToml(preloadScriptPath));\n await writeFile(wrapperScriptPath, generatePreloadWrapperScript(droidPath, droidDir));\n await chmod(wrapperScriptPath, 0o755);\n\n console.log(`[*] Created preload: ${preloadScriptPath}`);\n console.log(`[*] Created bunfig: ${bunfigPath}`);\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n return { preloadScript: preloadScriptPath, bunfigPath, wrapperScript: wrapperScriptPath };\n}\n\nexport function getPreloadScriptCode(): string {\n return generatePreloadScript();\n}\n","/**\n * Custom Model Manager\n * Manages custom models in ~/.factory/settings.json\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { styleText } from \"node:util\";\nimport { createInterface } from \"node:readline/promises\";\n\nconst FACTORY_DIR = join(homedir(), \".factory\");\nconst SETTINGS_PATH = join(FACTORY_DIR, \"settings.json\");\n\nexport type Provider = \"anthropic\" | \"openai\" | \"generic-chat-completion-api\";\n\nexport interface CustomModel {\n model: string;\n id: string;\n baseUrl: string;\n apiKey: string;\n displayName: string;\n provider: Provider;\n index: number;\n noImageSupport: boolean;\n}\n\ninterface FactorySettings {\n customModels?: CustomModel[];\n sessionDefaultSettings?: {\n model?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n/**\n * Generate model ID from displayName and index\n * Format: custom:{DisplayName}-{index} where spaces are replaced with -\n * This matches droid's buildCustomModelId function\n */\nexport function generateModelId(displayName: string, index: number): string {\n const normalized = displayName.trim().replace(/\\s+/g, \"-\");\n return `custom:${normalized}-${index}`;\n}\n\n/**\n * Load settings.json\n */\nexport function loadSettings(): FactorySettings {\n if (!existsSync(SETTINGS_PATH)) {\n return {};\n }\n try {\n const content = readFileSync(SETTINGS_PATH, \"utf-8\");\n return JSON.parse(content) as FactorySettings;\n } catch {\n return {};\n }\n}\n\n/**\n * Save settings.json\n */\nexport function saveSettings(settings: FactorySettings): void {\n if (!existsSync(FACTORY_DIR)) {\n mkdirSync(FACTORY_DIR, { recursive: true });\n }\n writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));\n}\n\n/**\n * Rebuild all model IDs and indexes based on their array position\n * This is necessary because droid uses array index as part of the ID\n */\nfunction rebuildModelIds(models: CustomModel[]): CustomModel[] {\n return models.map((model, index) => ({\n ...model,\n id: generateModelId(model.displayName, index),\n index,\n noImageSupport: model.noImageSupport ?? false,\n }));\n}\n\n/**\n * Update default model reference if needed\n */\nfunction updateDefaultModelRef(settings: FactorySettings, oldId: string, newId: string): void {\n if (settings.sessionDefaultSettings?.model === oldId) {\n settings.sessionDefaultSettings.model = newId;\n }\n}\n\n/**\n * Add a custom model at specified index (or end if not specified)\n */\nexport function addModel(\n modelName: string,\n displayName: string,\n baseUrl: string,\n apiKey: string,\n provider: Provider,\n insertIndex?: number,\n): { success: boolean; model: CustomModel; message: string } {\n const settings = loadSettings();\n\n if (!settings.customModels) {\n settings.customModels = [];\n }\n\n // Determine insert position\n const actualIndex = insertIndex ?? settings.customModels.length;\n if (actualIndex < 0 || actualIndex > settings.customModels.length) {\n return {\n success: false,\n model: {} as CustomModel,\n message: `Invalid index ${actualIndex}. Valid range: 0-${settings.customModels.length}`,\n };\n }\n\n const newModel: CustomModel = {\n model: modelName,\n id: \"\", // Will be set by rebuildModelIds\n baseUrl,\n apiKey,\n displayName,\n provider,\n index: 0, // Will be set by rebuildModelIds\n noImageSupport: false,\n };\n\n // Insert at specified position\n settings.customModels.splice(actualIndex, 0, newModel);\n\n // Rebuild all IDs\n settings.customModels = rebuildModelIds(settings.customModels);\n\n const insertedModel = settings.customModels[actualIndex];\n\n saveSettings(settings);\n\n return {\n success: true,\n model: insertedModel,\n message: `Added model \"${displayName}\" at index ${actualIndex} with ID \"${insertedModel.id}\"`,\n };\n}\n\n/**\n * Remove a custom model by index, ID, or displayName\n */\nexport function removeModel(identifier: string): {\n success: boolean;\n removed?: CustomModel;\n message: string;\n updatedModels?: { oldId: string; newId: string; displayName: string }[];\n} {\n const settings = loadSettings();\n\n if (!settings.customModels || settings.customModels.length === 0) {\n return {\n success: false,\n message: \"No custom models configured\",\n };\n }\n\n let index: number;\n\n // Try to parse as index number first\n const numericIndex = parseInt(identifier, 10);\n if (!isNaN(numericIndex) && numericIndex >= 0 && numericIndex < settings.customModels.length) {\n index = numericIndex;\n } else {\n // Find by ID or displayName\n index = settings.customModels.findIndex(\n (m) => m.id === identifier || m.displayName === identifier,\n );\n }\n\n if (index === -1) {\n return {\n success: false,\n message: `Model \"${identifier}\" not found. Use index (0-${settings.customModels.length - 1}), ID, or display name.`,\n };\n }\n\n // Store old IDs for tracking updates\n const oldIds = settings.customModels.map((m) => ({ id: m.id, displayName: m.displayName }));\n const removed = settings.customModels.splice(index, 1)[0];\n const oldDefaultId = settings.sessionDefaultSettings?.model;\n\n // Rebuild all IDs\n settings.customModels = rebuildModelIds(settings.customModels);\n\n // Track which models had their IDs updated\n const updatedModels: { oldId: string; newId: string; displayName: string }[] = [];\n for (let i = index; i < settings.customModels.length; i++) {\n const oldInfo = oldIds[i + 1]; // +1 because we removed one\n const newModel = settings.customModels[i];\n if (oldInfo && oldInfo.id !== newModel.id) {\n updatedModels.push({\n oldId: oldInfo.id,\n newId: newModel.id,\n displayName: newModel.displayName,\n });\n }\n }\n\n // Update default model reference if needed\n if (oldDefaultId) {\n if (oldDefaultId === removed.id) {\n // Default was the removed model, clear it\n delete settings.sessionDefaultSettings!.model;\n } else {\n // Check if default model's ID was updated\n const updatedDefault = updatedModels.find((u) => u.oldId === oldDefaultId);\n if (updatedDefault) {\n settings.sessionDefaultSettings!.model = updatedDefault.newId;\n }\n }\n }\n\n saveSettings(settings);\n\n return {\n success: true,\n removed,\n message: `Removed model \"${removed.displayName}\" (was at index ${index})`,\n updatedModels,\n };\n}\n\n/**\n * List all custom models\n */\nexport function listModels(): CustomModel[] {\n const settings = loadSettings();\n return settings.customModels || [];\n}\n\n/**\n * Get current default model\n */\nexport function getDefaultModel(): string | undefined {\n const settings = loadSettings();\n return settings.sessionDefaultSettings?.model;\n}\n\n/**\n * Print models list with detailed info\n */\nexport function printModelsList(): void {\n const models = listModels();\n const defaultModel = getDefaultModel();\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Custom Models\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n if (models.length === 0) {\n console.log(styleText(\"gray\", \" No custom models configured.\"));\n console.log();\n console.log(styleText(\"gray\", \" Add one with: npx droid-patch add-model\"));\n } else {\n console.log(styleText(\"white\", ` Found ${models.length} model(s):`));\n console.log();\n\n for (let i = 0; i < models.length; i++) {\n const model = models[i];\n const isDefault = model.id === defaultModel;\n const defaultMark = isDefault ? styleText(\"green\", \" [DEFAULT]\") : \"\";\n const indexMark = styleText(\"gray\", `[${i}]`);\n\n console.log(` ${indexMark} ${styleText([\"cyan\", \"bold\"], model.displayName)}${defaultMark}`);\n console.log(styleText(\"gray\", ` ID: ${model.id}`));\n console.log(styleText(\"gray\", ` Model: ${model.model}`));\n console.log(styleText(\"gray\", ` Provider: ${model.provider}`));\n console.log(styleText(\"gray\", ` Base URL: ${model.baseUrl}`));\n console.log(styleText(\"gray\", ` API Key: ${model.apiKey.substring(0, 8)}...`));\n console.log();\n }\n }\n\n console.log(styleText(\"gray\", ` Settings file: ${SETTINGS_PATH}`));\n console.log();\n}\n\n/**\n * Interactive prompt helper using readline/promises\n */\nasync function prompt(question: string, defaultValue?: string): Promise<string> {\n const defaultHint = defaultValue ? ` (${defaultValue})` : \"\";\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const answer = await rl.question(`${question}${defaultHint}: `);\n return answer.trim() || defaultValue || \"\";\n } finally {\n rl.close();\n }\n}\n\n/**\n * Interactive prompt for selecting from options\n */\nasync function promptSelect(question: string, options: string[]): Promise<string> {\n console.log(question);\n options.forEach((opt, i) => {\n console.log(styleText(\"cyan\", ` ${i + 1}. ${opt}`));\n });\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const answer = await rl.question(\"Select (number): \");\n const idx = parseInt(answer.trim(), 10) - 1;\n if (idx >= 0 && idx < options.length) {\n return options[idx];\n }\n return options[0];\n } finally {\n rl.close();\n }\n}\n\n/**\n * Interactive mode for adding a model\n */\nexport async function addModelInteractive(insertIndex?: number): Promise<void> {\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Add Custom Model (Interactive)\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n const models = listModels();\n if (models.length > 0) {\n console.log(styleText(\"gray\", `Current models: ${models.length}`));\n models.forEach((m, i) => {\n console.log(styleText(\"gray\", ` [${i}] ${m.displayName}`));\n });\n console.log();\n }\n\n const displayName = await prompt(\"Display name (e.g., 'Opus [proxy]')\");\n if (!displayName) {\n console.log(styleText(\"red\", \"Display name is required\"));\n return;\n }\n\n const modelName = await prompt(\"Model name (e.g., 'claude-sonnet-4-20250514')\");\n if (!modelName) {\n console.log(styleText(\"red\", \"Model name is required\"));\n return;\n }\n\n const baseUrl = await prompt(\"Base URL (e.g., 'http://127.0.0.1:20002/droid')\");\n if (!baseUrl) {\n console.log(styleText(\"red\", \"Base URL is required\"));\n return;\n }\n\n const apiKey = await prompt(\"API Key\");\n if (!apiKey) {\n console.log(styleText(\"red\", \"API Key is required\"));\n return;\n }\n\n const provider = (await promptSelect(\"Provider:\", [\n \"anthropic\",\n \"openai\",\n \"generic-chat-completion-api\",\n ])) as Provider;\n\n let actualIndex = insertIndex;\n if (actualIndex === undefined && models.length > 0) {\n const indexStr = await prompt(`Insert at index (0-${models.length})`, String(models.length));\n actualIndex = parseInt(indexStr, 10);\n if (isNaN(actualIndex) || actualIndex < 0 || actualIndex > models.length) {\n actualIndex = models.length;\n }\n }\n\n console.log();\n const result = addModel(modelName, displayName, baseUrl, apiKey, provider, actualIndex);\n\n if (result.success) {\n console.log(styleText(\"green\", `[+] ${result.message}`));\n console.log();\n console.log(styleText(\"white\", \"Model details:\"));\n console.log(styleText(\"gray\", ` ID: ${result.model.id}`));\n console.log(styleText(\"gray\", ` Display Name: ${result.model.displayName}`));\n console.log(styleText(\"gray\", ` Model: ${result.model.model}`));\n console.log(styleText(\"gray\", ` Provider: ${result.model.provider}`));\n console.log(styleText(\"gray\", ` Base URL: ${result.model.baseUrl}`));\n } else {\n console.log(styleText(\"red\", `Error: ${result.message}`));\n }\n}\n","import bin from \"tiny-bin\";\nimport { styleText } from \"node:util\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\nimport { patchDroid, type Patch } from \"./patcher.ts\";\nimport {\n createAlias,\n removeAlias,\n listAliases,\n createAliasForWrapper,\n clearAllAliases,\n removeAliasesByFilter,\n type FilterFlag,\n} from \"./alias.ts\";\nimport { createWebSearchUnifiedFiles } from \"./websearch-patch.ts\";\nimport {\n saveAliasMetadata,\n createMetadata,\n loadAliasMetadata,\n listAllMetadata,\n formatPatches,\n} from \"./metadata.ts\";\nimport {\n addModel,\n addModelInteractive,\n removeModel,\n printModelsList,\n type Provider,\n} from \"./model-manager.ts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst IS_WINDOWS = platform() === \"win32\";\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst version = getVersion();\n\nfunction getDroidVersion(droidPath: string): string | undefined {\n try {\n const result = execSync(`\"${droidPath}\" --version`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 5000,\n }).trim();\n // Parse version from output like \"droid 1.2.3\" or just \"1.2.3\"\n const match = result.match(/(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : result || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction findDefaultDroidPath(): string {\n const home = homedir();\n\n // Windows: use `where` command instead of `which`\n if (IS_WINDOWS) {\n try {\n const result = execSync(\"where droid\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n // `where` may return multiple lines, take the first one\n const firstResult = result.split(/\\r?\\n/)[0];\n if (firstResult && existsSync(firstResult)) {\n return firstResult;\n }\n } catch {\n // where command failed, continue with fallback paths\n }\n\n // Windows common installation paths\n const windowsPaths = [\n // Default install location\n join(home, \".droid\", \"bin\", \"droid.exe\"),\n // AppData local\n join(home, \"AppData\", \"Local\", \"Programs\", \"droid\", \"droid.exe\"),\n // Scoop\n join(home, \"scoop\", \"apps\", \"droid\", \"current\", \"droid.exe\"),\n // Current directory\n \"./droid.exe\",\n ];\n\n for (const p of windowsPaths) {\n if (existsSync(p)) return p;\n }\n\n // Return default path even if not found (will error later with helpful message)\n return join(home, \".droid\", \"bin\", \"droid.exe\");\n }\n\n // Unix: Try `which droid` first to find droid in PATH\n try {\n const result = execSync(\"which droid\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n if (result && existsSync(result)) {\n return result;\n }\n } catch {\n // which command failed, continue with fallback paths\n }\n\n // Common installation paths (Unix)\n const paths = [\n // Default sh install location\n join(home, \".droid\", \"bin\", \"droid\"),\n // Homebrew on Apple Silicon\n \"/opt/homebrew/bin/droid\",\n // Homebrew on Intel Mac / Linux\n \"/usr/local/bin/droid\",\n // Linux system-wide\n \"/usr/bin/droid\",\n // Current directory\n \"./droid\",\n ];\n\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n\n // Return default path even if not found (will error later with helpful message)\n return join(home, \".droid\", \"bin\", \"droid\");\n}\n\nbin(\"droid-patch\", \"CLI tool to patch droid binary with various modifications\")\n .package(\"droid-patch\", version)\n .option(\n \"--is-custom\",\n \"Patch isCustom:!0 to isCustom:!1 (enable context compression for custom models)\",\n )\n .option(\n \"--skip-login\",\n \"Inject a fake FACTORY_API_KEY to bypass login requirement (no real key needed)\",\n )\n .option(\n \"--api-base <url>\",\n \"Replace API URL (standalone: binary patch, max 22 chars; with --websearch: proxy forward target, no limit)\",\n )\n .option(\n \"--websearch\",\n \"Enable local WebSearch proxy with external providers (Smithery, Google PSE, etc.)\",\n )\n .option(\n \"--websearch-proxy\",\n \"Enable native provider websearch (requires proxy plugin, reads ~/.factory/settings.json)\",\n )\n .option(\"--standalone\", \"Standalone mode: mock non-LLM Factory APIs (use with --websearch)\")\n .option(\n \"--reasoning-effort\",\n \"Enable reasoning effort for custom models (set to high, enable UI selector)\",\n )\n .option(\n \"--disable-telemetry\",\n \"Disable telemetry and Sentry error reporting (block data uploads)\",\n )\n .option(\"--dry-run\", \"Verify patches without actually modifying the binary\")\n .option(\"-p, --path <path>\", \"Path to the droid binary\")\n .option(\"-o, --output <dir>\", \"Output directory for patched binary\")\n .option(\"--no-backup\", \"Do not create backup of original binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .argument(\"[alias]\", \"Alias name for the patched binary\")\n .action(async (options, args) => {\n const alias = args?.[0] as string | undefined;\n const isCustom = options[\"is-custom\"] as boolean;\n const skipLogin = options[\"skip-login\"] as boolean;\n const apiBase = options[\"api-base\"] as string | undefined;\n const websearch = options[\"websearch\"] as boolean;\n const websearchProxy = options[\"websearch-proxy\"] as boolean;\n const standalone = options[\"standalone\"] as boolean;\n // When --websearch is used with --api-base, forward to custom URL\n // Otherwise forward to official Factory API\n const websearchTarget = websearch ? apiBase || \"https://api.factory.ai\" : undefined;\n const reasoningEffort = options[\"reasoning-effort\"] as boolean;\n const noTelemetry = options[\"disable-telemetry\"] as boolean;\n const dryRun = options[\"dry-run\"] as boolean;\n const path = (options.path as string) || findDefaultDroidPath();\n const outputDir = options.output as string | undefined;\n const backup = options.backup !== false;\n const verbose = options.verbose as boolean;\n\n // If -o is specified with alias, output to that directory with alias name\n const outputPath = outputDir && alias ? join(outputDir, alias) : undefined;\n\n const needsBinaryPatch =\n !!isCustom ||\n !!skipLogin ||\n !!reasoningEffort ||\n !!noTelemetry ||\n (!!apiBase && !websearch && !websearchProxy);\n\n // Check for conflicting flags\n if (websearch && websearchProxy) {\n console.log(styleText(\"red\", \"Error: Cannot use --websearch and --websearch-proxy together\"));\n console.log(styleText(\"gray\", \"Choose one:\"));\n console.log(\n styleText(\"gray\", \" --websearch External providers (Smithery, Google PSE, etc.)\"),\n );\n console.log(\n styleText(\"gray\", \" --websearch-proxy Native provider (requires proxy plugin)\"),\n );\n process.exit(1);\n }\n\n // Wrapper-only mode (no binary patching needed):\n // - --websearch or --websearch-proxy (optional --standalone)\n const isWebsearchMode = websearch || websearchProxy;\n if (!needsBinaryPatch && isWebsearchMode) {\n if (!alias) {\n const flag = websearchProxy ? \"--websearch-proxy\" : \"--websearch\";\n console.log(styleText(\"red\", `Error: Alias name required for ${flag}`));\n console.log(styleText(\"gray\", `Usage: npx droid-patch ${flag} <alias>`));\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Wrapper Setup\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"white\", `WebSearch: native provider mode`));\n console.log(styleText(\"gray\", ` Requires proxy plugin (anthropic4droid)`));\n console.log(styleText(\"gray\", ` Reads model config from ~/.factory/settings.json`));\n } else if (websearch) {\n console.log(styleText(\"white\", `WebSearch: external providers mode`));\n console.log(styleText(\"white\", `Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", `Standalone mode: enabled`));\n }\n console.log();\n\n let execTargetPath = path;\n // Create websearch proxy files (proxy script + wrapper)\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n // For --websearch-proxy, apiBase comes from settings.json at runtime\n // For --websearch, use apiBase or default Factory API\n const forwardTarget = websearchProxy ? undefined : websearchTarget;\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n forwardTarget,\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n // Create alias pointing to outer wrapper\n const aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: false,\n skipLogin: false,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: false,\n noTelemetry: false,\n standalone: standalone,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" Wrapper Ready!\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(\"Run directly:\");\n console.log(styleText(\"yellow\", ` ${alias}`));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"Native Provider WebSearch (--websearch-proxy):\"));\n console.log(styleText(\"gray\", \" Uses model's built-in websearch via proxy plugin\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n console.log();\n console.log(styleText(\"yellow\", \"IMPORTANT: Requires proxy plugin (anthropic4droid)\"));\n console.log();\n console.log(\"Supported providers:\");\n console.log(styleText(\"yellow\", \" - anthropic: Claude web_search_20250305 server tool\"));\n console.log(styleText(\"yellow\", \" - openai: OpenAI web_search tool\"));\n console.log(styleText(\"gray\", \" - generic-chat-completion-api: Not supported\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1 # Basic logs\"));\n console.log(styleText(\"gray\", \" export DROID_SEARCH_VERBOSE=1 # Full request/response\"));\n } else if (websearch) {\n console.log(styleText(\"cyan\", \"External Providers WebSearch (--websearch):\"));\n console.log(\n styleText(\"gray\", \" Uses external search providers (Smithery, Google PSE, etc.)\"),\n );\n console.log();\n console.log(\"Search providers (in priority order):\");\n console.log(styleText(\"yellow\", \" 1. Smithery Exa (best quality):\"));\n console.log(styleText(\"gray\", \" export SMITHERY_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export SMITHERY_PROFILE=your_profile\"));\n console.log(styleText(\"gray\", \" 2. Google PSE:\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_CX=your_search_engine_id\"));\n console.log(styleText(\"gray\", \" 3. Tavily:\"));\n console.log(styleText(\"gray\", \" export TAVILY_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" 4-7. Serper, Brave, SearXNG, DuckDuckGo (fallbacks)\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1\"));\n }\n return;\n }\n\n if (!isCustom && !skipLogin && !apiBase && !websearch && !reasoningEffort && !noTelemetry) {\n console.log(styleText(\"yellow\", \"No patch flags specified. Available patches:\"));\n console.log(styleText(\"gray\", \" --is-custom Patch isCustom for custom models\"));\n console.log(\n styleText(\"gray\", \" --skip-login Bypass login by injecting a fake API key\"),\n );\n console.log(\n styleText(\n \"gray\",\n \" --api-base Replace API URL (standalone: max 22 chars; with --websearch: no limit)\",\n ),\n );\n console.log(styleText(\"gray\", \" --websearch Enable local WebSearch proxy\"));\n console.log(\n styleText(\"gray\", \" --reasoning-effort Set reasoning effort level for custom models\"),\n );\n console.log(\n styleText(\"gray\", \" --disable-telemetry Disable telemetry and Sentry error reporting\"),\n );\n console.log(\n styleText(\"gray\", \" --standalone Standalone mode: mock non-LLM Factory APIs\"),\n );\n console.log();\n console.log(\"Usage examples:\");\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom droid-custom\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --skip-login droid-nologin\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom --skip-login droid-patched\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch droid-search\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch --standalone droid-local\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --disable-telemetry droid-private\"));\n console.log(\n styleText(\n \"cyan\",\n \" npx droid-patch --websearch --api-base=http://127.0.0.1:20002 my-droid\",\n ),\n );\n process.exit(1);\n }\n\n if (!alias && !dryRun) {\n console.log(styleText(\"red\", \"Error: alias name is required\"));\n console.log(\n styleText(\n \"gray\",\n \"Usage: droid-patch [--is-custom] [--skip-login] [-o <dir>] <alias-name>\",\n ),\n );\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Binary Patcher\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n const patches: Patch[] = [];\n if (isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n // Add skip-login patch: replace process.env.FACTORY_API_KEY with a fixed fake key\n // \"process.env.FACTORY_API_KEY\" is 27 chars, we replace with \"fk-droid-patch-skip-00000\" (25 chars + quotes = 27)\n if (skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: 'Replace process.env.FACTORY_API_KEY with \"fk-droid-patch-skip-00000\"',\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Add api-base patch: replace the Factory API base URL\n // Original: \"https://api.factory.ai\" (22 chars)\n // We need to pad the replacement URL to be exactly 22 chars\n // Note: When --websearch is used, --api-base sets the forward target instead of binary patching\n if (apiBase && !websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const originalLength = originalUrl.length; // 22 chars\n\n // Validate and normalize the URL\n let normalizedUrl = apiBase.replace(/\\/+$/, \"\"); // Remove trailing slashes\n\n if (normalizedUrl.length > originalLength) {\n console.log(\n styleText(\"red\", `Error: API base URL must be ${originalLength} characters or less`),\n );\n console.log(\n styleText(\"gray\", ` Your URL: \"${normalizedUrl}\" (${normalizedUrl.length} chars)`),\n );\n console.log(styleText(\"gray\", ` Maximum: ${originalLength} characters`));\n console.log();\n console.log(styleText(\"yellow\", \"Tip: Use a shorter URL or set up a local redirect.\"));\n console.log(styleText(\"gray\", \" Examples:\"));\n console.log(styleText(\"gray\", \" http://127.0.0.1:3000 (19 chars)\"));\n console.log(styleText(\"gray\", \" http://localhost:80 (19 chars)\"));\n process.exit(1);\n }\n\n // Pad the URL with spaces at the end to match original length\n // Note: trailing spaces in URL are generally ignored\n const paddedUrl = normalizedUrl.padEnd(originalLength, \" \");\n\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${normalizedUrl}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n // Add reasoning-effort patch: set custom models to use \"high\" reasoning\n // Also modify UI conditions to show reasoning selector for custom models\n if (reasoningEffort) {\n // [\"none\"] is 8 chars, [\"high\"] is 8 chars - perfect match!\n patches.push({\n name: \"reasoningEffortSupported\",\n description: 'Change supportedReasoningEfforts:[\"none\"] to [\"high\"]',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n\n // \"none\" is 4 chars, \"high\" is 4 chars - perfect match!\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n\n // Change UI condition from length>1 to length>0\n // This allows custom models with single reasoning option to show the selector\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n\n // Change UI condition from length<=1 to length<=0\n // This enables the reasoning setting in /settings menu for custom models\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n\n // Bypass reasoning effort validation to allow settings.json override\n // This allows \"xhigh\" in settings.json to work even though default is \"high\"\n // Pattern varies by version:\n // v0.39.0+: T!==\"none\"&&T!==\"off\"&&!X.supportedReasoningEfforts.includes(T)\n // v0.43.0+: T!==\"none\"&&T!==\"off\"&&!R.reasoningEffort.supported.includes(T)\n // v0.49.0+: !this.validateReasoningEffort(D,H.reasoningEffort)\n // Using regex to match any single-letter variable (A-Z) and preserve property path\n // Logic: && 0 && makes entire condition always false, bypassing validation\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n // Regex captures:\n // $1 = reasoning effort variable (single letter)\n // $2 = model/config variable (single letter)\n // $3 = property path (supportedReasoningEfforts or reasoningEffort.supported)\n regexPattern:\n /([A-Z])!==\"none\"&&\\1!==\"off\"&&!([A-Z])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n regexReplacement: '$1!=\"none\"&&$1!=\"off\"&&0&&$2.$3.includes($1)',\n alreadyPatchedRegexPattern:\n /([A-Z])!=\"none\"&&\\1!=\"off\"&&0&&([A-Z])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n });\n }\n\n // Add no-telemetry patches: disable telemetry uploads and Sentry error reporting\n // Strategy:\n // 1. Break environment variable names so Sentry is never initialized (Q1() returns false)\n // 2. Invert flushToWeb condition so it returns early without making any fetch request\n if (noTelemetry) {\n // Patch 1: Break Sentry environment variable checks\n // Q1() function checks: VITE_VERCEL_ENV, ENABLE_SENTRY, NEXT_PUBLIC_ENABLE_SENTRY, FACTORY_ENABLE_SENTRY\n // By changing first letter to X, the env vars will never match, so Q1() returns false\n // and Sentry is never initialized\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n\n // Patch 2: Make flushToWeb always return early to prevent ANY fetch request\n // Original: if(this.webEvents.length===0)return; // returns only when empty\n // Changed: if(!0||this.webEvents.length)return; // !0=true, ALWAYS returns\n // Result: Function always exits immediately, no telemetry is ever sent\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n try {\n const result = await patchDroid({\n inputPath: path,\n outputPath: outputPath,\n patches,\n dryRun,\n backup,\n verbose,\n });\n\n if (dryRun) {\n console.log();\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"gray\", \"To apply the patches, run without --dry-run:\"));\n console.log(styleText(\"cyan\", ` npx droid-patch --is-custom ${alias || \"<alias-name>\"}`));\n process.exit(0);\n }\n\n // If -o is specified, just output the file without creating alias\n if (outputDir && result.success && result.outputPath) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", `Patched binary saved to: ${result.outputPath}`));\n process.exit(0);\n }\n\n if (result.success && result.outputPath && alias) {\n console.log();\n\n let execTargetPath = result.outputPath;\n\n if (websearch || websearchProxy) {\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n websearchProxy ? undefined : websearchTarget, // websearchProxy reads from settings.json at runtime\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"WebSearch enabled (native provider mode)\"));\n console.log(styleText(\"gray\", \" Requires proxy plugin (anthropic4droid)\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n } else {\n console.log(styleText(\"cyan\", \"WebSearch enabled (external providers mode)\"));\n console.log(styleText(\"white\", ` Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", ` Standalone mode: enabled`));\n }\n }\n\n let aliasResult;\n if (websearch || websearchProxy) {\n aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n } else {\n aliasResult = await createAlias(result.outputPath, alias, verbose);\n }\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: !!isCustom,\n skipLogin: !!skipLogin,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: !!reasoningEffort,\n noTelemetry: !!noTelemetry,\n standalone: !!standalone,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n }\n\n if (result.success) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n }\n\n process.exit(result.success ? 0 : 1);\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n if (verbose) console.error((error as Error).stack);\n process.exit(1);\n }\n })\n .command(\"list\", \"List all droid-patch aliases\")\n .action(async () => {\n await listAliases();\n })\n .command(\"remove\", \"Remove alias(es) by name or filter\")\n .argument(\"[alias-or-path]\", \"Alias name or file path to remove\")\n .option(\"--patch-version <version>\", \"Remove aliases created by this droid-patch version\")\n .option(\"--droid-version <version>\", \"Remove aliases for this droid version\")\n .option(\n \"--flag <flag>\",\n \"Remove aliases with this flag (is-custom, skip-login, websearch, api-base, reasoning-effort, disable-telemetry, standalone)\",\n )\n .action(async (options, args) => {\n const target = args?.[0] as string | undefined;\n const patchVersion = options[\"patch-version\"] as string | undefined;\n const droidVersion = options[\"droid-version\"] as string | undefined;\n const flagRaw = options.flag as string | undefined;\n let flag: FilterFlag | undefined;\n if (flagRaw) {\n const allowedFlags: FilterFlag[] = [\n \"is-custom\",\n \"skip-login\",\n \"websearch\",\n \"api-base\",\n \"reasoning-effort\",\n \"disable-telemetry\",\n \"standalone\",\n ];\n if (!allowedFlags.includes(flagRaw as FilterFlag)) {\n console.error(styleText(\"red\", `Error: Invalid --flag value: ${flagRaw}`));\n console.error(styleText(\"gray\", `Allowed: ${allowedFlags.join(\", \")}`));\n process.exit(1);\n }\n flag = flagRaw as FilterFlag;\n }\n\n // If filter options are provided, use filter mode\n if (patchVersion || droidVersion || flag) {\n await removeAliasesByFilter({\n patchVersion,\n droidVersion,\n flags: flag ? [flag] : undefined,\n });\n return;\n }\n\n // If no target and no filter, show error\n if (!target) {\n console.error(\n styleText(\n \"red\",\n \"Error: Provide an alias name or use filter options (--patch-version, --droid-version, --flag)\",\n ),\n );\n process.exit(1);\n }\n\n // Check if it's a file path (contains / or .)\n if (target.includes(\"/\") || existsSync(target)) {\n // It's a file path, delete directly\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(target);\n console.log(styleText(\"green\", `[*] Removed: ${target}`));\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n process.exit(1);\n }\n } else {\n // It's an alias name\n await removeAlias(target);\n }\n })\n .command(\"version\", \"Print droid-patch version\")\n .action(() => {\n console.log(`droid-patch v${version}`);\n })\n .command(\"clear\", \"Remove all droid-patch aliases and related files\")\n .action(async () => {\n await clearAllAliases();\n })\n .command(\"update\", \"Update aliases with latest droid binary\")\n .argument(\"[alias]\", \"Specific alias to update (optional, updates all if not specified)\")\n .option(\"--dry-run\", \"Preview without making changes\")\n .option(\"-p, --path <path>\", \"Path to new droid binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .action(async (options, args) => {\n const aliasName = args?.[0] as string | undefined;\n const dryRun = options[\"dry-run\"] as boolean;\n const newBinaryPath = (options.path as string) || findDefaultDroidPath();\n const verbose = options.verbose as boolean;\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid-Patch Update\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Verify the new binary exists\n if (!existsSync(newBinaryPath)) {\n console.log(styleText(\"red\", `Error: Droid binary not found at ${newBinaryPath}`));\n console.log(styleText(\"gray\", \"Use -p to specify a different path\"));\n process.exit(1);\n }\n\n // Get aliases to update\n let metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[];\n if (aliasName) {\n const meta = await loadAliasMetadata(aliasName);\n if (!meta) {\n console.log(styleText(\"red\", `Error: No metadata found for alias \"${aliasName}\"`));\n console.log(\n styleText(\"gray\", \"This alias may have been created before update tracking was added.\"),\n );\n console.log(styleText(\"gray\", \"Remove and recreate the alias to enable update support.\"));\n process.exit(1);\n }\n metaList = [meta];\n } else {\n metaList = await listAllMetadata();\n if (metaList.length === 0) {\n console.log(styleText(\"yellow\", \"No aliases with metadata found.\"));\n console.log(styleText(\"gray\", \"Create aliases with droid-patch to enable update support.\"));\n process.exit(0);\n }\n }\n\n console.log(styleText(\"white\", `Using droid binary: ${newBinaryPath}`));\n console.log(styleText(\"white\", `Found ${metaList.length} alias(es) to update`));\n if (dryRun) {\n console.log(styleText(\"blue\", \"(DRY RUN - no changes will be made)\"));\n }\n console.log();\n\n let successCount = 0;\n let failCount = 0;\n\n for (const meta of metaList) {\n if (!meta) continue;\n\n console.log(styleText(\"cyan\", `─`.repeat(40)));\n console.log(styleText(\"white\", `Updating: ${styleText([\"cyan\", \"bold\"], meta.name)}`));\n console.log(styleText(\"gray\", ` Patches: ${formatPatches(meta.patches)}`));\n\n if (dryRun) {\n console.log(styleText(\"blue\", ` [DRY RUN] Would re-apply patches`));\n successCount++;\n continue;\n }\n\n try {\n // Build patch list based on metadata\n const patches: Patch[] = [];\n\n if (meta.patches.isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n if (meta.patches.skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: \"Replace process.env.FACTORY_API_KEY with fake key\",\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Only apply apiBase binary patch when NOT using websearch\n // When websearch is enabled, apiBase is used as forward target, not binary patch\n if (meta.patches.apiBase && !meta.patches.websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const paddedUrl = meta.patches.apiBase.padEnd(originalUrl.length, \" \");\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${meta.patches.apiBase}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n if (meta.patches.reasoningEffort) {\n patches.push({\n name: \"reasoningEffortSupported\",\n description: 'Change supportedReasoningEfforts:[\"none\"] to [\"high\"]',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n regexPattern:\n /([A-Z])!==\"none\"&&\\1!==\"off\"&&!([A-Z])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n regexReplacement: '$1!=\"none\"&&$1!=\"off\"&&0&&$2.$3.includes($1)',\n alreadyPatchedRegexPattern:\n /([A-Z])!=\"none\"&&\\1!=\"off\"&&0&&([A-Z])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n });\n }\n\n if (meta.patches.noTelemetry) {\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n // Determine output path based on whether this is a websearch alias\n const binsDir = join(homedir(), \".droid-patch\", \"bins\");\n const outputPath = join(binsDir, `${meta.name}-patched`);\n\n // Apply patches (only if there are binary patches to apply)\n if (patches.length > 0) {\n const result = await patchDroid({\n inputPath: newBinaryPath,\n outputPath,\n patches,\n dryRun: false,\n backup: false,\n verbose,\n });\n\n if (!result.success) {\n console.log(styleText(\"red\", ` ✗ Failed to apply patches`));\n failCount++;\n continue;\n }\n\n // Re-sign on macOS\n if (process.platform === \"darwin\") {\n try {\n const { execSync } = await import(\"node:child_process\");\n execSync(`codesign --force --deep --sign - \"${outputPath}\"`, {\n stdio: \"pipe\",\n });\n if (verbose) {\n console.log(styleText(\"gray\", ` Re-signed binary`));\n }\n } catch {\n console.log(styleText(\"yellow\", ` [!] Could not re-sign binary`));\n }\n }\n }\n\n let execTargetPath = patches.length > 0 ? outputPath : newBinaryPath;\n\n // If websearch is enabled, regenerate wrapper files\n // Support both new 'websearch' field and old 'proxy' field for backward compatibility\n const hasWebsearch = meta.patches.websearch || !!meta.patches.proxy;\n if (hasWebsearch) {\n // Determine forward target: apiBase > proxy (legacy) > default\n const forwardTarget =\n meta.patches.apiBase || meta.patches.proxy || \"https://api.factory.ai\";\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n meta.name,\n forwardTarget,\n meta.patches.standalone || false,\n );\n execTargetPath = wrapperScript;\n if (verbose) {\n console.log(styleText(\"gray\", ` Regenerated websearch wrapper`));\n if (meta.patches.standalone) {\n console.log(styleText(\"gray\", ` Standalone mode: enabled`));\n }\n }\n // Migrate old proxy field to new websearch field\n if (meta.patches.proxy && !meta.patches.websearch) {\n meta.patches.websearch = true;\n meta.patches.apiBase = meta.patches.proxy;\n delete meta.patches.proxy;\n }\n }\n\n // If this alias previously used removed features (statusline/sessions), drop legacy flags\n // so the updated alias points directly to the new target wrapper/binary.\n delete (meta.patches as Record<string, unknown>).statusline;\n delete (meta.patches as Record<string, unknown>).sessions;\n\n // Update symlink - find existing or use stored aliasPath\n const { symlink, unlink, readlink, lstat } = await import(\"node:fs/promises\");\n let aliasPath = meta.aliasPath;\n\n // If aliasPath not stored (old version), try to find existing symlink\n if (!aliasPath) {\n const commonPathDirs = [\n join(homedir(), \".local/bin\"),\n join(homedir(), \"bin\"),\n join(homedir(), \".bin\"),\n \"/opt/homebrew/bin\",\n \"/usr/local/bin\",\n join(homedir(), \".droid-patch\", \"aliases\"),\n ];\n\n for (const dir of commonPathDirs) {\n const possiblePath = join(dir, meta.name);\n if (existsSync(possiblePath)) {\n try {\n const stats = await lstat(possiblePath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(possiblePath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasPath = possiblePath;\n if (verbose) {\n console.log(styleText(\"gray\", ` Found existing symlink: ${aliasPath}`));\n }\n break;\n }\n }\n } catch {\n // Ignore errors, continue searching\n }\n }\n }\n }\n\n // Update symlink if we have a path\n if (aliasPath) {\n try {\n if (existsSync(aliasPath)) {\n const currentTarget = await readlink(aliasPath);\n if (currentTarget !== execTargetPath) {\n await unlink(aliasPath);\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Updated symlink: ${aliasPath}`));\n }\n }\n } else {\n // Symlink doesn't exist, recreate it\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Recreated symlink: ${aliasPath}`));\n }\n }\n // Store aliasPath in metadata for future updates\n meta.aliasPath = aliasPath;\n } catch (symlinkError) {\n console.log(\n styleText(\n \"yellow\",\n ` [!] Could not update symlink: ${(symlinkError as Error).message}`,\n ),\n );\n }\n }\n\n // Update metadata\n meta.updatedAt = new Date().toISOString();\n meta.originalBinaryPath = newBinaryPath;\n meta.droidVersion = getDroidVersion(newBinaryPath);\n meta.droidPatchVersion = version;\n await saveAliasMetadata(meta);\n\n console.log(styleText(\"green\", ` ✓ Updated successfully`));\n successCount++;\n } catch (error) {\n console.log(styleText(\"red\", ` ✗ Error: ${(error as Error).message}`));\n if (verbose) {\n console.error((error as Error).stack);\n }\n failCount++;\n }\n }\n\n console.log();\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n if (dryRun) {\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"gray\", ` Would update ${successCount} alias(es)`));\n } else if (failCount === 0) {\n console.log(styleText([\"green\", \"bold\"], \" UPDATE COMPLETE\"));\n console.log(styleText(\"gray\", ` Updated ${successCount} alias(es)`));\n } else {\n console.log(styleText([\"yellow\", \"bold\"], \" UPDATE FINISHED WITH ERRORS\"));\n console.log(styleText(\"gray\", ` Success: ${successCount}, Failed: ${failCount}`));\n }\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n })\n .command(\"add-model\", \"Add a custom model to settings.json (interactive if no options)\")\n .option(\"-m, --model <model>\", \"Model name (e.g., claude-sonnet-4-20250514)\")\n .option(\"-n, --name <name>\", \"Display name (e.g., 'Opus [proxy]')\")\n .option(\"-u, --url <url>\", \"Base URL (e.g., http://127.0.0.1:20002/droid)\")\n .option(\"-k, --key <key>\", \"API key\")\n .option(\n \"-p, --provider <provider>\",\n \"Provider: anthropic, openai, or generic-chat-completion-api\",\n )\n .option(\"-i, --index <index>\", \"Insert at index (auto-assigned if not specified)\")\n .action(async (options) => {\n const model = options.model as string | undefined;\n const displayName = options.name as string | undefined;\n const baseUrl = options.url as string | undefined;\n const apiKey = options.key as string | undefined;\n const providerStr = options.provider as string | undefined;\n const indexStr = options.index as string | undefined;\n\n // If no options provided, enter interactive mode\n if (!model && !displayName && !baseUrl && !apiKey && !providerStr) {\n const index = indexStr ? parseInt(indexStr, 10) : undefined;\n await addModelInteractive(index);\n return;\n }\n\n // If some but not all options provided, show error with usage\n if (!model || !displayName || !baseUrl || !apiKey || !providerStr) {\n console.log(\n styleText(\n \"yellow\",\n \"Missing required options. Enter interactive mode or provide all options.\",\n ),\n );\n console.log();\n console.log(styleText(\"white\", \"Interactive mode:\"));\n console.log(styleText(\"cyan\", \" npx droid-patch add-model\"));\n console.log();\n console.log(styleText(\"white\", \"Full command mode:\"));\n console.log(styleText(\"cyan\", \" npx droid-patch add-model \\\\\"));\n console.log(styleText(\"cyan\", ' -m \"claude-sonnet-4-20250514\" \\\\'));\n console.log(styleText(\"cyan\", ' -n \"Opus [proxy]\" \\\\'));\n console.log(styleText(\"cyan\", ' -u \"http://127.0.0.1:20002/droid\" \\\\'));\n console.log(styleText(\"cyan\", ' -k \"your-api-key\" \\\\'));\n console.log(styleText(\"cyan\", ' -p \"anthropic\"'));\n console.log();\n console.log(styleText(\"gray\", \"Providers: anthropic, openai, generic-chat-completion-api\"));\n console.log(styleText(\"gray\", \"Optional: -i <index> to insert at specific position\"));\n process.exit(1);\n }\n\n const validProviders: Provider[] = [\"anthropic\", \"openai\", \"generic-chat-completion-api\"];\n if (!validProviders.includes(providerStr as Provider)) {\n console.log(styleText(\"red\", `Error: Invalid provider \"${providerStr}\"`));\n console.log(styleText(\"gray\", `Valid providers: ${validProviders.join(\", \")}`));\n process.exit(1);\n }\n\n const index = indexStr ? parseInt(indexStr, 10) : undefined;\n if (indexStr && (isNaN(index!) || index! < 0)) {\n console.log(styleText(\"red\", \"Error: Index must be a non-negative number\"));\n process.exit(1);\n }\n\n const result = addModel(model, displayName, baseUrl, apiKey, providerStr as Provider, index);\n\n if (result.success) {\n console.log(styleText(\"green\", `[+] ${result.message}`));\n console.log();\n console.log(styleText(\"white\", \"Model details:\"));\n console.log(styleText(\"gray\", ` ID: ${result.model.id}`));\n console.log(styleText(\"gray\", ` Display Name: ${result.model.displayName}`));\n console.log(styleText(\"gray\", ` Model: ${result.model.model}`));\n console.log(styleText(\"gray\", ` Provider: ${result.model.provider}`));\n console.log(styleText(\"gray\", ` Base URL: ${result.model.baseUrl}`));\n } else {\n console.log(styleText(\"red\", `Error: ${result.message}`));\n process.exit(1);\n }\n })\n .command(\"remove-model\", \"Remove a custom model from settings.json\")\n .argument(\"<identifier>\", \"Model index, ID, or display name to remove\")\n .action((options, args) => {\n const identifier = args?.[0] as string;\n\n if (!identifier) {\n console.log(styleText(\"red\", \"Error: Model identifier required\"));\n console.log();\n console.log(styleText(\"white\", \"Usage:\"));\n console.log(styleText(\"cyan\", \" npx droid-patch remove-model <identifier>\"));\n console.log();\n console.log(styleText(\"gray\", \"Identifier can be:\"));\n console.log(styleText(\"gray\", \" - Index number (e.g., 0, 1, 2)\"));\n console.log(styleText(\"gray\", \" - Model ID (e.g., custom:Opus-[proxy]-0)\"));\n console.log(styleText(\"gray\", \" - Display name (e.g., 'Opus [proxy]')\"));\n console.log();\n console.log(styleText(\"gray\", \"Use 'npx droid-patch list-models' to see all models\"));\n process.exit(1);\n }\n\n const result = removeModel(identifier);\n\n if (result.success && result.removed) {\n console.log(styleText(\"green\", `[+] ${result.message}`));\n console.log();\n console.log(styleText(\"white\", \"Removed model details:\"));\n console.log(styleText(\"gray\", ` ID: ${result.removed.id}`));\n console.log(styleText(\"gray\", ` Display Name: ${result.removed.displayName}`));\n console.log(styleText(\"gray\", ` Model: ${result.removed.model}`));\n console.log(styleText(\"gray\", ` Provider: ${result.removed.provider}`));\n\n if (result.updatedModels && result.updatedModels.length > 0) {\n console.log();\n console.log(styleText(\"yellow\", \"Updated model IDs (due to index shift):\"));\n for (const updated of result.updatedModels) {\n console.log(styleText(\"gray\", ` ${updated.displayName}:`));\n console.log(styleText(\"gray\", ` ${updated.oldId} → ${updated.newId}`));\n }\n }\n console.log();\n console.log(styleText(\"gray\", \"Use 'npx droid-patch list-models' to see current state.\"));\n } else {\n console.log(styleText(\"red\", `Error: ${result.message}`));\n process.exit(1);\n }\n })\n .command(\"list-models\", \"List all custom models in settings.json\")\n .action(() => {\n printModelsList();\n })\n .run()\n .catch((err: Error) => {\n console.error(err);\n process.exit(1);\n });\n"],"mappings":";;;;;;;;;;;;;;;;;AAMA,SAAgB,gCAAwC;AACtD,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+OT,SAAgB,kCACd,gBAAwB,0BAChB;AAER,QADa,+BAA+B,CAChC,QACV,iDACA,wBAAwB,cAAc,IACvC;;;;;;;;;;;;;;;;;AChPH,SAAgB,gCACd,gBAAwB,0BAChB;AACR,QAAO;;;;;;;;;;;;;;uBAcc,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACXrC,MAAMA,eAAa,UAAU,KAAK;;;;AA4GlC,SAAS,uBACP,WACA,iBACA,aAAsB,OACd;CACR,MAAM,gBAAgB,aAAa,uBAAuB;AAC1D,QAAO;;;;gBAIO,gBAAgB;aACnB,UAAU;;;cAGT,aAAa,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAiCjC,cAAc;;IAEd,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsClB,SAAS,uBACP,WACA,iBACA,aAAsB,OACd;AAER,QAAO;;;;;oBAKW,gBAAgB;iBACnB,UAAU;;;;;;;;;;;;;;;;;;;;;;;EAPH,aAAa,8BAA8B,GA8BnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DhB,eAAsB,4BACpB,WACA,WACA,WACA,SACA,aAAsB,OACtB,oBAA6B,OAC8B;AAC3D,KAAI,CAAC,WAAW,UAAU,CACxB,OAAM,MAAM,WAAW,EAAE,WAAW,MAAM,CAAC;CAG7C,MAAM,kBAAkB,KAAK,WAAW,GAAG,UAAU,WAAW;CAEhE,MAAM,oBAAoBA,eACtB,KAAK,WAAW,GAAG,UAAU,MAAM,GACnC,KAAK,WAAW,UAAU;CAE9B,MAAM,gBAAgB,WAAW;AAKjC,OAAM,UAAU,iBAJE,oBACd,gCAAgC,cAAc,GAC9C,kCAAkC,cAAc,CAET;AAC3C,SAAQ,IAAI,6BAA6B,kBAAkB;AAC3D,SAAQ,IACN,aAAa,oBAAoB,4CAA4C,uBAC9E;AAOD,OAAM,UAAU,mBAJIA,eAChB,uBAAuB,WAAW,iBAAiB,WAAW,GAC9D,uBAAuB,WAAW,iBAAiB,WAAW,CAEnB;AAC/C,KAAI,CAACA,aACH,OAAM,MAAM,mBAAmB,IAAM;AAEvC,SAAQ,IAAI,wBAAwB,oBAAoB;AAExD,KAAI,WACF,SAAQ,IAAI,8BAA8B;AAG5C,QAAO;EACL,eAAe;EACf,eAAe;EAChB;;;;;;;;;AC5VH,MAAM,cAAc,KAAK,SAAS,EAAE,WAAW;AAC/C,MAAM,gBAAgB,KAAK,aAAa,gBAAgB;;;;;;AA6BxD,SAAgB,gBAAgB,aAAqB,OAAuB;AAE1E,QAAO,UADY,YAAY,MAAM,CAAC,QAAQ,QAAQ,IAAI,CAC9B,GAAG;;;;;AAMjC,SAAgB,eAAgC;AAC9C,KAAI,CAAC,WAAW,cAAc,CAC5B,QAAO,EAAE;AAEX,KAAI;EACF,MAAM,UAAU,aAAa,eAAe,QAAQ;AACpD,SAAO,KAAK,MAAM,QAAQ;SACpB;AACN,SAAO,EAAE;;;;;;AAOb,SAAgB,aAAa,UAAiC;AAC5D,KAAI,CAAC,WAAW,YAAY,CAC1B,WAAU,aAAa,EAAE,WAAW,MAAM,CAAC;AAE7C,eAAc,eAAe,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC;;;;;;AAOjE,SAAS,gBAAgB,QAAsC;AAC7D,QAAO,OAAO,KAAK,OAAO,WAAW;EACnC,GAAG;EACH,IAAI,gBAAgB,MAAM,aAAa,MAAM;EAC7C;EACA,gBAAgB,MAAM,kBAAkB;EACzC,EAAE;;;;;AAeL,SAAgB,SACd,WACA,aACA,SACA,QACA,UACA,aAC2D;CAC3D,MAAM,WAAW,cAAc;AAE/B,KAAI,CAAC,SAAS,aACZ,UAAS,eAAe,EAAE;CAI5B,MAAM,cAAc,eAAe,SAAS,aAAa;AACzD,KAAI,cAAc,KAAK,cAAc,SAAS,aAAa,OACzD,QAAO;EACL,SAAS;EACT,OAAO,EAAE;EACT,SAAS,iBAAiB,YAAY,mBAAmB,SAAS,aAAa;EAChF;CAGH,MAAMC,WAAwB;EAC5B,OAAO;EACP,IAAI;EACJ;EACA;EACA;EACA;EACA,OAAO;EACP,gBAAgB;EACjB;AAGD,UAAS,aAAa,OAAO,aAAa,GAAG,SAAS;AAGtD,UAAS,eAAe,gBAAgB,SAAS,aAAa;CAE9D,MAAM,gBAAgB,SAAS,aAAa;AAE5C,cAAa,SAAS;AAEtB,QAAO;EACL,SAAS;EACT,OAAO;EACP,SAAS,gBAAgB,YAAY,aAAa,YAAY,YAAY,cAAc,GAAG;EAC5F;;;;;AAMH,SAAgB,YAAY,YAK1B;CACA,MAAM,WAAW,cAAc;AAE/B,KAAI,CAAC,SAAS,gBAAgB,SAAS,aAAa,WAAW,EAC7D,QAAO;EACL,SAAS;EACT,SAAS;EACV;CAGH,IAAIC;CAGJ,MAAM,eAAe,SAAS,YAAY,GAAG;AAC7C,KAAI,CAAC,MAAM,aAAa,IAAI,gBAAgB,KAAK,eAAe,SAAS,aAAa,OACpF,SAAQ;KAGR,SAAQ,SAAS,aAAa,WAC3B,MAAM,EAAE,OAAO,cAAc,EAAE,gBAAgB,WACjD;AAGH,KAAI,UAAU,GACZ,QAAO;EACL,SAAS;EACT,SAAS,UAAU,WAAW,4BAA4B,SAAS,aAAa,SAAS,EAAE;EAC5F;CAIH,MAAM,SAAS,SAAS,aAAa,KAAK,OAAO;EAAE,IAAI,EAAE;EAAI,aAAa,EAAE;EAAa,EAAE;CAC3F,MAAM,UAAU,SAAS,aAAa,OAAO,OAAO,EAAE,CAAC;CACvD,MAAM,eAAe,SAAS,wBAAwB;AAGtD,UAAS,eAAe,gBAAgB,SAAS,aAAa;CAG9D,MAAMC,gBAAyE,EAAE;AACjF,MAAK,IAAI,IAAI,OAAO,IAAI,SAAS,aAAa,QAAQ,KAAK;EACzD,MAAM,UAAU,OAAO,IAAI;EAC3B,MAAM,WAAW,SAAS,aAAa;AACvC,MAAI,WAAW,QAAQ,OAAO,SAAS,GACrC,eAAc,KAAK;GACjB,OAAO,QAAQ;GACf,OAAO,SAAS;GAChB,aAAa,SAAS;GACvB,CAAC;;AAKN,KAAI,aACF,KAAI,iBAAiB,QAAQ,GAE3B,QAAO,SAAS,uBAAwB;MACnC;EAEL,MAAM,iBAAiB,cAAc,MAAM,MAAM,EAAE,UAAU,aAAa;AAC1E,MAAI,eACF,UAAS,uBAAwB,QAAQ,eAAe;;AAK9D,cAAa,SAAS;AAEtB,QAAO;EACL,SAAS;EACT;EACA,SAAS,kBAAkB,QAAQ,YAAY,kBAAkB,MAAM;EACvE;EACD;;;;;AAMH,SAAgB,aAA4B;AAE1C,QADiB,cAAc,CACf,gBAAgB,EAAE;;;;;AAMpC,SAAgB,kBAAsC;AAEpD,QADiB,cAAc,CACf,wBAAwB;;;;;AAM1C,SAAgB,kBAAwB;CACtC,MAAM,SAAS,YAAY;CAC3B,MAAM,eAAe,iBAAiB;AAEtC,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,IAAI,UAAU,CAAC,QAAQ,OAAO,EAAE,kBAAkB,CAAC;AAC3D,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,KAAK;AAEb,KAAI,OAAO,WAAW,GAAG;AACvB,UAAQ,IAAI,UAAU,QAAQ,iCAAiC,CAAC;AAChE,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,QAAQ,4CAA4C,CAAC;QACtE;AACL,UAAQ,IAAI,UAAU,SAAS,WAAW,OAAO,OAAO,YAAY,CAAC;AACrE,UAAQ,KAAK;AAEb,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;GACtC,MAAM,QAAQ,OAAO;GAErB,MAAM,cADY,MAAM,OAAO,eACC,UAAU,SAAS,aAAa,GAAG;GACnE,MAAM,YAAY,UAAU,QAAQ,IAAI,EAAE,GAAG;AAE7C,WAAQ,IAAI,KAAK,UAAU,GAAG,UAAU,CAAC,QAAQ,OAAO,EAAE,MAAM,YAAY,GAAG,cAAc;AAC7F,WAAQ,IAAI,UAAU,QAAQ,mBAAmB,MAAM,KAAK,CAAC;AAC7D,WAAQ,IAAI,UAAU,QAAQ,mBAAmB,MAAM,QAAQ,CAAC;AAChE,WAAQ,IAAI,UAAU,QAAQ,mBAAmB,MAAM,WAAW,CAAC;AACnE,WAAQ,IAAI,UAAU,QAAQ,mBAAmB,MAAM,UAAU,CAAC;AAClE,WAAQ,IAAI,UAAU,QAAQ,mBAAmB,MAAM,OAAO,UAAU,GAAG,EAAE,CAAC,KAAK,CAAC;AACpF,WAAQ,KAAK;;;AAIjB,SAAQ,IAAI,UAAU,QAAQ,oBAAoB,gBAAgB,CAAC;AACnE,SAAQ,KAAK;;;;;AAMf,eAAe,OAAO,UAAkB,cAAwC;CAC9E,MAAM,cAAc,eAAe,KAAK,aAAa,KAAK;CAC1D,MAAM,KAAK,gBAAgB;EACzB,OAAO,QAAQ;EACf,QAAQ,QAAQ;EACjB,CAAC;AAEF,KAAI;AAEF,UADe,MAAM,GAAG,SAAS,GAAG,WAAW,YAAY,IAAI,EACjD,MAAM,IAAI,gBAAgB;WAChC;AACR,KAAG,OAAO;;;;;;AAOd,eAAe,aAAa,UAAkB,SAAoC;AAChF,SAAQ,IAAI,SAAS;AACrB,SAAQ,SAAS,KAAK,MAAM;AAC1B,UAAQ,IAAI,UAAU,QAAQ,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC;GACpD;CAEF,MAAM,KAAK,gBAAgB;EACzB,OAAO,QAAQ;EACf,QAAQ,QAAQ;EACjB,CAAC;AAEF,KAAI;EACF,MAAM,SAAS,MAAM,GAAG,SAAS,oBAAoB;EACrD,MAAM,MAAM,SAAS,OAAO,MAAM,EAAE,GAAG,GAAG;AAC1C,MAAI,OAAO,KAAK,MAAM,QAAQ,OAC5B,QAAO,QAAQ;AAEjB,SAAO,QAAQ;WACP;AACR,KAAG,OAAO;;;;;;AAOd,eAAsB,oBAAoB,aAAqC;AAC7E,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,IAAI,UAAU,CAAC,QAAQ,OAAO,EAAE,mCAAmC,CAAC;AAC5E,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,KAAK;CAEb,MAAM,SAAS,YAAY;AAC3B,KAAI,OAAO,SAAS,GAAG;AACrB,UAAQ,IAAI,UAAU,QAAQ,mBAAmB,OAAO,SAAS,CAAC;AAClE,SAAO,SAAS,GAAG,MAAM;AACvB,WAAQ,IAAI,UAAU,QAAQ,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC;IAC3D;AACF,UAAQ,KAAK;;CAGf,MAAM,cAAc,MAAM,OAAO,sCAAsC;AACvE,KAAI,CAAC,aAAa;AAChB,UAAQ,IAAI,UAAU,OAAO,2BAA2B,CAAC;AACzD;;CAGF,MAAM,YAAY,MAAM,OAAO,gDAAgD;AAC/E,KAAI,CAAC,WAAW;AACd,UAAQ,IAAI,UAAU,OAAO,yBAAyB,CAAC;AACvD;;CAGF,MAAM,UAAU,MAAM,OAAO,kDAAkD;AAC/E,KAAI,CAAC,SAAS;AACZ,UAAQ,IAAI,UAAU,OAAO,uBAAuB,CAAC;AACrD;;CAGF,MAAM,SAAS,MAAM,OAAO,UAAU;AACtC,KAAI,CAAC,QAAQ;AACX,UAAQ,IAAI,UAAU,OAAO,sBAAsB,CAAC;AACpD;;CAGF,MAAM,WAAY,MAAM,aAAa,aAAa;EAChD;EACA;EACA;EACD,CAAC;CAEF,IAAI,cAAc;AAClB,KAAI,gBAAgB,UAAa,OAAO,SAAS,GAAG;EAClD,MAAM,WAAW,MAAM,OAAO,sBAAsB,OAAO,OAAO,IAAI,OAAO,OAAO,OAAO,CAAC;AAC5F,gBAAc,SAAS,UAAU,GAAG;AACpC,MAAI,MAAM,YAAY,IAAI,cAAc,KAAK,cAAc,OAAO,OAChE,eAAc,OAAO;;AAIzB,SAAQ,KAAK;CACb,MAAM,SAAS,SAAS,WAAW,aAAa,SAAS,QAAQ,UAAU,YAAY;AAEvF,KAAI,OAAO,SAAS;AAClB,UAAQ,IAAI,UAAU,SAAS,OAAO,OAAO,UAAU,CAAC;AACxD,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,SAAS,iBAAiB,CAAC;AACjD,UAAQ,IAAI,UAAU,QAAQ,SAAS,OAAO,MAAM,KAAK,CAAC;AAC1D,UAAQ,IAAI,UAAU,QAAQ,mBAAmB,OAAO,MAAM,cAAc,CAAC;AAC7E,UAAQ,IAAI,UAAU,QAAQ,YAAY,OAAO,MAAM,QAAQ,CAAC;AAChE,UAAQ,IAAI,UAAU,QAAQ,eAAe,OAAO,MAAM,WAAW,CAAC;AACtE,UAAQ,IAAI,UAAU,QAAQ,eAAe,OAAO,MAAM,UAAU,CAAC;OAErE,SAAQ,IAAI,UAAU,OAAO,UAAU,OAAO,UAAU,CAAC;;;;;ACjX7D,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AACzD,MAAM,aAAa,UAAU,KAAK;AAElC,SAAS,aAAqB;AAC5B,KAAI;EACF,MAAM,UAAU,KAAK,WAAW,MAAM,eAAe;AAErD,SADY,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC,CAC3C,WAAW;SAChB;AACN,SAAO;;;AAIX,MAAM,UAAU,YAAY;AAE5B,SAAS,gBAAgB,WAAuC;AAC9D,KAAI;EACF,MAAM,SAAS,SAAS,IAAI,UAAU,cAAc;GAClD,UAAU;GACV,OAAO;IAAC;IAAQ;IAAQ;IAAO;GAC/B,SAAS;GACV,CAAC,CAAC,MAAM;EAET,MAAM,QAAQ,OAAO,MAAM,kBAAkB;AAC7C,SAAO,QAAQ,MAAM,KAAK,UAAU;SAC9B;AACN;;;AAIJ,SAAS,uBAA+B;CACtC,MAAM,OAAO,SAAS;AAGtB,KAAI,YAAY;AACd,MAAI;GAMF,MAAM,cALS,SAAS,eAAe;IACrC,UAAU;IACV,OAAO;KAAC;KAAQ;KAAQ;KAAO;IAChC,CAAC,CAAC,MAAM,CAEkB,MAAM,QAAQ,CAAC;AAC1C,OAAI,eAAe,WAAW,YAAY,CACxC,QAAO;UAEH;EAKR,MAAM,eAAe;GAEnB,KAAK,MAAM,UAAU,OAAO,YAAY;GAExC,KAAK,MAAM,WAAW,SAAS,YAAY,SAAS,YAAY;GAEhE,KAAK,MAAM,SAAS,QAAQ,SAAS,WAAW,YAAY;GAE5D;GACD;AAED,OAAK,MAAM,KAAK,aACd,KAAI,WAAW,EAAE,CAAE,QAAO;AAI5B,SAAO,KAAK,MAAM,UAAU,OAAO,YAAY;;AAIjD,KAAI;EACF,MAAM,SAAS,SAAS,eAAe;GACrC,UAAU;GACV,OAAO;IAAC;IAAQ;IAAQ;IAAO;GAChC,CAAC,CAAC,MAAM;AACT,MAAI,UAAU,WAAW,OAAO,CAC9B,QAAO;SAEH;CAKR,MAAM,QAAQ;EAEZ,KAAK,MAAM,UAAU,OAAO,QAAQ;EAEpC;EAEA;EAEA;EAEA;EACD;AAED,MAAK,MAAM,KAAK,MACd,KAAI,WAAW,EAAE,CAAE,QAAO;AAI5B,QAAO,KAAK,MAAM,UAAU,OAAO,QAAQ;;AAG7C,IAAI,eAAe,4DAA4D,CAC5E,QAAQ,eAAe,QAAQ,CAC/B,OACC,eACA,kFACD,CACA,OACC,gBACA,iFACD,CACA,OACC,oBACA,6GACD,CACA,OACC,eACA,oFACD,CACA,OACC,qBACA,2FACD,CACA,OAAO,gBAAgB,oEAAoE,CAC3F,OACC,sBACA,8EACD,CACA,OACC,uBACA,oEACD,CACA,OAAO,aAAa,uDAAuD,CAC3E,OAAO,qBAAqB,2BAA2B,CACvD,OAAO,sBAAsB,sCAAsC,CACnE,OAAO,eAAe,0CAA0C,CAChE,OAAO,iBAAiB,wBAAwB,CAChD,SAAS,WAAW,oCAAoC,CACxD,OAAO,OAAO,SAAS,SAAS;CAC/B,MAAM,QAAQ,OAAO;CACrB,MAAM,WAAW,QAAQ;CACzB,MAAM,YAAY,QAAQ;CAC1B,MAAM,UAAU,QAAQ;CACxB,MAAM,YAAY,QAAQ;CAC1B,MAAM,iBAAiB,QAAQ;CAC/B,MAAM,aAAa,QAAQ;CAG3B,MAAM,kBAAkB,YAAY,WAAW,2BAA2B;CAC1E,MAAM,kBAAkB,QAAQ;CAChC,MAAM,cAAc,QAAQ;CAC5B,MAAM,SAAS,QAAQ;CACvB,MAAM,OAAQ,QAAQ,QAAmB,sBAAsB;CAC/D,MAAM,YAAY,QAAQ;CAC1B,MAAM,SAAS,QAAQ,WAAW;CAClC,MAAM,UAAU,QAAQ;CAGxB,MAAM,aAAa,aAAa,QAAQ,KAAK,WAAW,MAAM,GAAG;CAEjE,MAAM,mBACJ,CAAC,CAAC,YACF,CAAC,CAAC,aACF,CAAC,CAAC,mBACF,CAAC,CAAC,eACD,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC;AAG/B,KAAI,aAAa,gBAAgB;AAC/B,UAAQ,IAAI,UAAU,OAAO,+DAA+D,CAAC;AAC7F,UAAQ,IAAI,UAAU,QAAQ,cAAc,CAAC;AAC7C,UAAQ,IACN,UAAU,QAAQ,uEAAuE,CAC1F;AACD,UAAQ,IACN,UAAU,QAAQ,+DAA+D,CAClF;AACD,UAAQ,KAAK,EAAE;;AAMjB,KAAI,CAAC,qBADmB,aAAa,iBACK;AACxC,MAAI,CAAC,OAAO;GACV,MAAM,OAAO,iBAAiB,sBAAsB;AACpD,WAAQ,IAAI,UAAU,OAAO,kCAAkC,OAAO,CAAC;AACvE,WAAQ,IAAI,UAAU,QAAQ,0BAA0B,KAAK,UAAU,CAAC;AACxE,WAAQ,KAAK,EAAE;;AAGjB,UAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,UAAQ,IAAI,UAAU,CAAC,QAAQ,OAAO,EAAE,wBAAwB,CAAC;AACjE,UAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,UAAQ,KAAK;AACb,MAAI,gBAAgB;AAClB,WAAQ,IAAI,UAAU,SAAS,kCAAkC,CAAC;AAClE,WAAQ,IAAI,UAAU,QAAQ,4CAA4C,CAAC;AAC3E,WAAQ,IAAI,UAAU,QAAQ,qDAAqD,CAAC;aAC3E,WAAW;AACpB,WAAQ,IAAI,UAAU,SAAS,qCAAqC,CAAC;AACrE,WAAQ,IAAI,UAAU,SAAS,mBAAmB,kBAAkB,CAAC;;AAEvE,MAAI,WACF,SAAQ,IAAI,UAAU,SAAS,2BAA2B,CAAC;AAE7D,UAAQ,KAAK;EAEb,IAAI,iBAAiB;EAMrB,MAAM,EAAE,kBAAkB,MAAM,4BAJf,KAAK,SAAS,EAAE,gBAAgB,QAAQ,EAMvD,gBACA,OAJoB,iBAAiB,SAAY,iBAMjD,YACA,eACD;AACD,mBAAiB;EAGjB,MAAM,cAAc,MAAM,sBAAsB,gBAAgB,OAAO,QAAQ;EAG/E,MAAM,eAAe,gBAAgB,KAAK;AAoB1C,QAAM,kBAnBW,eACf,OACA,MACA;GACE,UAAU;GACV,WAAW;GACX,SAAS,WAAW;GACpB,WAAW,CAAC,CAAC;GACb,gBAAgB,CAAC,CAAC;GAClB,iBAAiB;GACjB,aAAa;GACD;GACb,EACD;GACE,mBAAmB;GACnB;GACA,WAAW,YAAY;GACxB,CACF,CACgC;AAEjC,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;AAC/C,UAAQ,IAAI,UAAU,CAAC,SAAS,OAAO,EAAE,mBAAmB,CAAC;AAC7D,UAAQ,IAAI,UAAU,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;AAC/C,UAAQ,KAAK;AACb,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,IAAI,UAAU,UAAU,KAAK,QAAQ,CAAC;AAC9C,UAAQ,KAAK;AACb,MAAI,gBAAgB;AAClB,WAAQ,IAAI,UAAU,QAAQ,iDAAiD,CAAC;AAChF,WAAQ,IAAI,UAAU,QAAQ,qDAAqD,CAAC;AACpF,WAAQ,IAAI,UAAU,QAAQ,qDAAqD,CAAC;AACpF,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,UAAU,qDAAqD,CAAC;AACtF,WAAQ,KAAK;AACb,WAAQ,IAAI,uBAAuB;AACnC,WAAQ,IAAI,UAAU,UAAU,wDAAwD,CAAC;AACzF,WAAQ,IAAI,UAAU,UAAU,qCAAqC,CAAC;AACtE,WAAQ,IAAI,UAAU,QAAQ,iDAAiD,CAAC;AAChF,WAAQ,KAAK;AACb,WAAQ,IAAI,cAAc;AAC1B,WAAQ,IAAI,UAAU,QAAQ,gDAAgD,CAAC;AAC/E,WAAQ,IAAI,UAAU,QAAQ,2DAA2D,CAAC;aACjF,WAAW;AACpB,WAAQ,IAAI,UAAU,QAAQ,8CAA8C,CAAC;AAC7E,WAAQ,IACN,UAAU,QAAQ,gEAAgE,CACnF;AACD,WAAQ,KAAK;AACb,WAAQ,IAAI,wCAAwC;AACpD,WAAQ,IAAI,UAAU,UAAU,oCAAoC,CAAC;AACrE,WAAQ,IAAI,UAAU,QAAQ,4CAA4C,CAAC;AAC3E,WAAQ,IAAI,UAAU,QAAQ,4CAA4C,CAAC;AAC3E,WAAQ,IAAI,UAAU,QAAQ,mBAAmB,CAAC;AAClD,WAAQ,IAAI,UAAU,QAAQ,8CAA8C,CAAC;AAC7E,WAAQ,IAAI,UAAU,QAAQ,kDAAkD,CAAC;AACjF,WAAQ,IAAI,UAAU,QAAQ,eAAe,CAAC;AAC9C,WAAQ,IAAI,UAAU,QAAQ,0CAA0C,CAAC;AACzE,WAAQ,IAAI,UAAU,QAAQ,wDAAwD,CAAC;AACvF,WAAQ,KAAK;AACb,WAAQ,IAAI,cAAc;AAC1B,WAAQ,IAAI,UAAU,QAAQ,gCAAgC,CAAC;;AAEjE;;AAGF,KAAI,CAAC,YAAY,CAAC,aAAa,CAAC,WAAW,CAAC,aAAa,CAAC,mBAAmB,CAAC,aAAa;AACzF,UAAQ,IAAI,UAAU,UAAU,+CAA+C,CAAC;AAChF,UAAQ,IAAI,UAAU,QAAQ,yDAAyD,CAAC;AACxF,UAAQ,IACN,UAAU,QAAQ,iEAAiE,CACpF;AACD,UAAQ,IACN,UACE,QACA,+FACD,CACF;AACD,UAAQ,IAAI,UAAU,QAAQ,qDAAqD,CAAC;AACpF,UAAQ,IACN,UAAU,QAAQ,qEAAqE,CACxF;AACD,UAAQ,IACN,UAAU,QAAQ,qEAAqE,CACxF;AACD,UAAQ,IACN,UAAU,QAAQ,mEAAmE,CACtF;AACD,UAAQ,KAAK;AACb,UAAQ,IAAI,kBAAkB;AAC9B,UAAQ,IAAI,UAAU,QAAQ,6CAA6C,CAAC;AAC5E,UAAQ,IAAI,UAAU,QAAQ,+CAA+C,CAAC;AAC9E,UAAQ,IAAI,UAAU,QAAQ,2DAA2D,CAAC;AAC1F,UAAQ,IAAI,UAAU,QAAQ,6CAA6C,CAAC;AAC5E,UAAQ,IAAI,UAAU,QAAQ,yDAAyD,CAAC;AACxF,UAAQ,IAAI,UAAU,QAAQ,sDAAsD,CAAC;AACrF,UAAQ,IACN,UACE,QACA,2EACD,CACF;AACD,UAAQ,KAAK,EAAE;;AAGjB,KAAI,CAAC,SAAS,CAAC,QAAQ;AACrB,UAAQ,IAAI,UAAU,OAAO,gCAAgC,CAAC;AAC9D,UAAQ,IACN,UACE,QACA,0EACD,CACF;AACD,UAAQ,KAAK,EAAE;;AAGjB,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,IAAI,UAAU,CAAC,QAAQ,OAAO,EAAE,yBAAyB,CAAC;AAClE,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,KAAK;CAEb,MAAMC,UAAmB,EAAE;AAC3B,KAAI,SACF,SAAQ,KAAK;EACX,MAAM;EACN,aAAa;EACb,SAAS,OAAO,KAAK,cAAc;EACnC,aAAa,OAAO,KAAK,cAAc;EACxC,CAAC;AAKJ,KAAI,UACF,SAAQ,KAAK;EACX,MAAM;EACN,aAAa;EACb,SAAS,OAAO,KAAK,8BAA8B;EACnD,aAAa,OAAO,KAAK,gCAA8B;EACxD,CAAC;AAOJ,KAAI,WAAW,CAAC,WAAW;EACzB,MAAM,cAAc;EACpB,MAAM,iBAAiB;EAGvB,IAAI,gBAAgB,QAAQ,QAAQ,QAAQ,GAAG;AAE/C,MAAI,cAAc,SAAS,gBAAgB;AACzC,WAAQ,IACN,UAAU,OAAO,+BAA+B,eAAe,qBAAqB,CACrF;AACD,WAAQ,IACN,UAAU,QAAQ,gBAAgB,cAAc,KAAK,cAAc,OAAO,SAAS,CACpF;AACD,WAAQ,IAAI,UAAU,QAAQ,eAAe,eAAe,aAAa,CAAC;AAC1E,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,UAAU,qDAAqD,CAAC;AACtF,WAAQ,IAAI,UAAU,QAAQ,cAAc,CAAC;AAC7C,WAAQ,IAAI,UAAU,QAAQ,uCAAuC,CAAC;AACtE,WAAQ,IAAI,UAAU,QAAQ,sCAAsC,CAAC;AACrE,WAAQ,KAAK,EAAE;;EAKjB,MAAM,YAAY,cAAc,OAAO,gBAAgB,IAAI;AAE3D,UAAQ,KAAK;GACX,MAAM;GACN,aAAa,iCAAiC,cAAc;GAC5D,SAAS,OAAO,KAAK,YAAY;GACjC,aAAa,OAAO,KAAK,UAAU;GACpC,CAAC;;AAKJ,KAAI,iBAAiB;AAEnB,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,uCAAqC;GAC1D,aAAa,OAAO,KAAK,uCAAqC;GAC/D,CAAC;AAGF,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,kCAAgC;GACrD,aAAa,OAAO,KAAK,kCAAgC;GAC1D,CAAC;AAIF,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,qCAAqC;GAC1D,aAAa,OAAO,KAAK,qCAAqC;GAC/D,CAAC;AAIF,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,sCAAsC;GAC3D,aAAa,OAAO,KAAK,sCAAsC;GAChE,CAAC;AAUF,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,GAAG;GACxB,aAAa,OAAO,KAAK,GAAG;GAK5B,cACE;GACF,kBAAkB;GAClB,4BACE;GACH,CAAC;;AAOJ,KAAI,aAAa;AAKf,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,gBAAgB;GACrC,aAAa,OAAO,KAAK,gBAAgB;GAC1C,CAAC;AAEF,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,kBAAkB;GACvC,aAAa,OAAO,KAAK,kBAAkB;GAC5C,CAAC;AAMF,UAAQ,KAAK;GACX,MAAM;GACN,aAAa;GACb,SAAS,OAAO,KAAK,4BAA4B;GACjD,aAAa,OAAO,KAAK,4BAA4B;GACtD,CAAC;;AAGJ,KAAI;EACF,MAAM,SAAS,MAAM,WAAW;GAC9B,WAAW;GACC;GACZ;GACA;GACA;GACA;GACD,CAAC;AAEF,MAAI,QAAQ;AACV,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,WAAQ,IAAI,UAAU,CAAC,QAAQ,OAAO,EAAE,qBAAqB,CAAC;AAC9D,WAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,QAAQ,+CAA+C,CAAC;AAC9E,WAAQ,IAAI,UAAU,QAAQ,iCAAiC,SAAS,iBAAiB,CAAC;AAC1F,WAAQ,KAAK,EAAE;;AAIjB,MAAI,aAAa,OAAO,WAAW,OAAO,YAAY;AACpD,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;AAC/C,WAAQ,IAAI,UAAU,CAAC,SAAS,OAAO,EAAE,qBAAqB,CAAC;AAC/D,WAAQ,IAAI,UAAU,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;AAC/C,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,SAAS,4BAA4B,OAAO,aAAa,CAAC;AAChF,WAAQ,KAAK,EAAE;;AAGjB,MAAI,OAAO,WAAW,OAAO,cAAc,OAAO;AAChD,WAAQ,KAAK;GAEb,IAAI,iBAAiB,OAAO;AAE5B,OAAI,aAAa,gBAAgB;IAE/B,MAAM,EAAE,kBAAkB,MAAM,4BADf,KAAK,SAAS,EAAE,gBAAgB,QAAQ,EAGvD,gBACA,OACA,iBAAiB,SAAY,iBAC7B,YACA,eACD;AACD,qBAAiB;AAEjB,YAAQ,KAAK;AACb,QAAI,gBAAgB;AAClB,aAAQ,IAAI,UAAU,QAAQ,2CAA2C,CAAC;AAC1E,aAAQ,IAAI,UAAU,QAAQ,4CAA4C,CAAC;AAC3E,aAAQ,IAAI,UAAU,QAAQ,qDAAqD,CAAC;WAC/E;AACL,aAAQ,IAAI,UAAU,QAAQ,8CAA8C,CAAC;AAC7E,aAAQ,IAAI,UAAU,SAAS,qBAAqB,kBAAkB,CAAC;;AAEzE,QAAI,WACF,SAAQ,IAAI,UAAU,SAAS,6BAA6B,CAAC;;GAIjE,IAAI;AACJ,OAAI,aAAa,eACf,eAAc,MAAM,sBAAsB,gBAAgB,OAAO,QAAQ;OAEzE,eAAc,MAAM,YAAY,OAAO,YAAY,OAAO,QAAQ;GAIpE,MAAM,eAAe,gBAAgB,KAAK;AAoB1C,SAAM,kBAnBW,eACf,OACA,MACA;IACE,UAAU,CAAC,CAAC;IACZ,WAAW,CAAC,CAAC;IACb,SAAS,WAAW;IACpB,WAAW,CAAC,CAAC;IACb,gBAAgB,CAAC,CAAC;IAClB,iBAAiB,CAAC,CAAC;IACnB,aAAa,CAAC,CAAC;IACf,YAAY,CAAC,CAAC;IACf,EACD;IACE,mBAAmB;IACnB;IACA,WAAW,YAAY;IACxB,CACF,CACgC;;AAGnC,MAAI,OAAO,SAAS;AAClB,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;AAC/C,WAAQ,IAAI,UAAU,CAAC,SAAS,OAAO,EAAE,qBAAqB,CAAC;AAC/D,WAAQ,IAAI,UAAU,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;;AAGjD,UAAQ,KAAK,OAAO,UAAU,IAAI,EAAE;UAC7B,OAAO;AACd,UAAQ,MAAM,UAAU,OAAO,UAAW,MAAgB,UAAU,CAAC;AACrE,MAAI,QAAS,SAAQ,MAAO,MAAgB,MAAM;AAClD,UAAQ,KAAK,EAAE;;EAEjB,CACD,QAAQ,QAAQ,+BAA+B,CAC/C,OAAO,YAAY;AAClB,OAAM,aAAa;EACnB,CACD,QAAQ,UAAU,qCAAqC,CACvD,SAAS,mBAAmB,oCAAoC,CAChE,OAAO,6BAA6B,qDAAqD,CACzF,OAAO,6BAA6B,wCAAwC,CAC5E,OACC,iBACA,8HACD,CACA,OAAO,OAAO,SAAS,SAAS;CAC/B,MAAM,SAAS,OAAO;CACtB,MAAM,eAAe,QAAQ;CAC7B,MAAM,eAAe,QAAQ;CAC7B,MAAM,UAAU,QAAQ;CACxB,IAAIC;AACJ,KAAI,SAAS;EACX,MAAMC,eAA6B;GACjC;GACA;GACA;GACA;GACA;GACA;GACA;GACD;AACD,MAAI,CAAC,aAAa,SAAS,QAAsB,EAAE;AACjD,WAAQ,MAAM,UAAU,OAAO,gCAAgC,UAAU,CAAC;AAC1E,WAAQ,MAAM,UAAU,QAAQ,YAAY,aAAa,KAAK,KAAK,GAAG,CAAC;AACvE,WAAQ,KAAK,EAAE;;AAEjB,SAAO;;AAIT,KAAI,gBAAgB,gBAAgB,MAAM;AACxC,QAAM,sBAAsB;GAC1B;GACA;GACA,OAAO,OAAO,CAAC,KAAK,GAAG;GACxB,CAAC;AACF;;AAIF,KAAI,CAAC,QAAQ;AACX,UAAQ,MACN,UACE,OACA,gGACD,CACF;AACD,UAAQ,KAAK,EAAE;;AAIjB,KAAI,OAAO,SAAS,IAAI,IAAI,WAAW,OAAO,EAAE;EAE9C,MAAM,EAAE,qBAAW,MAAM,OAAO;AAChC,MAAI;AACF,SAAMC,SAAO,OAAO;AACpB,WAAQ,IAAI,UAAU,SAAS,gBAAgB,SAAS,CAAC;WAClD,OAAO;AACd,WAAQ,MAAM,UAAU,OAAO,UAAW,MAAgB,UAAU,CAAC;AACrE,WAAQ,KAAK,EAAE;;OAIjB,OAAM,YAAY,OAAO;EAE3B,CACD,QAAQ,WAAW,4BAA4B,CAC/C,aAAa;AACZ,SAAQ,IAAI,gBAAgB,UAAU;EACtC,CACD,QAAQ,SAAS,mDAAmD,CACpE,OAAO,YAAY;AAClB,OAAM,iBAAiB;EACvB,CACD,QAAQ,UAAU,0CAA0C,CAC5D,SAAS,WAAW,oEAAoE,CACxF,OAAO,aAAa,iCAAiC,CACrD,OAAO,qBAAqB,2BAA2B,CACvD,OAAO,iBAAiB,wBAAwB,CAChD,OAAO,OAAO,SAAS,SAAS;CAC/B,MAAM,YAAY,OAAO;CACzB,MAAM,SAAS,QAAQ;CACvB,MAAM,gBAAiB,QAAQ,QAAmB,sBAAsB;CACxE,MAAM,UAAU,QAAQ;AAExB,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,IAAI,UAAU,CAAC,QAAQ,OAAO,EAAE,uBAAuB,CAAC;AAChE,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,SAAQ,KAAK;AAGb,KAAI,CAAC,WAAW,cAAc,EAAE;AAC9B,UAAQ,IAAI,UAAU,OAAO,oCAAoC,gBAAgB,CAAC;AAClF,UAAQ,IAAI,UAAU,QAAQ,qCAAqC,CAAC;AACpE,UAAQ,KAAK,EAAE;;CAIjB,IAAIC;AACJ,KAAI,WAAW;EACb,MAAM,OAAO,MAAM,kBAAkB,UAAU;AAC/C,MAAI,CAAC,MAAM;AACT,WAAQ,IAAI,UAAU,OAAO,uCAAuC,UAAU,GAAG,CAAC;AAClF,WAAQ,IACN,UAAU,QAAQ,qEAAqE,CACxF;AACD,WAAQ,IAAI,UAAU,QAAQ,0DAA0D,CAAC;AACzF,WAAQ,KAAK,EAAE;;AAEjB,aAAW,CAAC,KAAK;QACZ;AACL,aAAW,MAAM,iBAAiB;AAClC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAQ,IAAI,UAAU,UAAU,kCAAkC,CAAC;AACnE,WAAQ,IAAI,UAAU,QAAQ,4DAA4D,CAAC;AAC3F,WAAQ,KAAK,EAAE;;;AAInB,SAAQ,IAAI,UAAU,SAAS,uBAAuB,gBAAgB,CAAC;AACvE,SAAQ,IAAI,UAAU,SAAS,SAAS,SAAS,OAAO,sBAAsB,CAAC;AAC/E,KAAI,OACF,SAAQ,IAAI,UAAU,QAAQ,sCAAsC,CAAC;AAEvE,SAAQ,KAAK;CAEb,IAAI,eAAe;CACnB,IAAI,YAAY;AAEhB,MAAK,MAAM,QAAQ,UAAU;AAC3B,MAAI,CAAC,KAAM;AAEX,UAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,UAAQ,IAAI,UAAU,SAAS,aAAa,UAAU,CAAC,QAAQ,OAAO,EAAE,KAAK,KAAK,GAAG,CAAC;AACtF,UAAQ,IAAI,UAAU,QAAQ,cAAc,cAAc,KAAK,QAAQ,GAAG,CAAC;AAE3E,MAAI,QAAQ;AACV,WAAQ,IAAI,UAAU,QAAQ,qCAAqC,CAAC;AACpE;AACA;;AAGF,MAAI;GAEF,MAAMJ,UAAmB,EAAE;AAE3B,OAAI,KAAK,QAAQ,SACf,SAAQ,KAAK;IACX,MAAM;IACN,aAAa;IACb,SAAS,OAAO,KAAK,cAAc;IACnC,aAAa,OAAO,KAAK,cAAc;IACxC,CAAC;AAGJ,OAAI,KAAK,QAAQ,UACf,SAAQ,KAAK;IACX,MAAM;IACN,aAAa;IACb,SAAS,OAAO,KAAK,8BAA8B;IACnD,aAAa,OAAO,KAAK,gCAA8B;IACxD,CAAC;AAKJ,OAAI,KAAK,QAAQ,WAAW,CAAC,KAAK,QAAQ,WAAW;IACnD,MAAM,cAAc;IACpB,MAAM,YAAY,KAAK,QAAQ,QAAQ,OAAO,IAAoB,IAAI;AACtE,YAAQ,KAAK;KACX,MAAM;KACN,aAAa,iCAAiC,KAAK,QAAQ,QAAQ;KACnE,SAAS,OAAO,KAAK,YAAY;KACjC,aAAa,OAAO,KAAK,UAAU;KACpC,CAAC;;AAGJ,OAAI,KAAK,QAAQ,iBAAiB;AAChC,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,uCAAqC;KAC1D,aAAa,OAAO,KAAK,uCAAqC;KAC/D,CAAC;AACF,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,kCAAgC;KACrD,aAAa,OAAO,KAAK,kCAAgC;KAC1D,CAAC;AACF,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,qCAAqC;KAC1D,aAAa,OAAO,KAAK,qCAAqC;KAC/D,CAAC;AACF,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,sCAAsC;KAC3D,aAAa,OAAO,KAAK,sCAAsC;KAChE,CAAC;AACF,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,GAAG;KACxB,aAAa,OAAO,KAAK,GAAG;KAC5B,cACE;KACF,kBAAkB;KAClB,4BACE;KACH,CAAC;;AAGJ,OAAI,KAAK,QAAQ,aAAa;AAC5B,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,gBAAgB;KACrC,aAAa,OAAO,KAAK,gBAAgB;KAC1C,CAAC;AACF,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,kBAAkB;KACvC,aAAa,OAAO,KAAK,kBAAkB;KAC5C,CAAC;AACF,YAAQ,KAAK;KACX,MAAM;KACN,aAAa;KACb,SAAS,OAAO,KAAK,4BAA4B;KACjD,aAAa,OAAO,KAAK,4BAA4B;KACtD,CAAC;;GAKJ,MAAM,aAAa,KADH,KAAK,SAAS,EAAE,gBAAgB,OAAO,EACtB,GAAG,KAAK,KAAK,UAAU;AAGxD,OAAI,QAAQ,SAAS,GAAG;AAUtB,QAAI,EATW,MAAM,WAAW;KAC9B,WAAW;KACX;KACA;KACA,QAAQ;KACR,QAAQ;KACR;KACD,CAAC,EAEU,SAAS;AACnB,aAAQ,IAAI,UAAU,OAAO,8BAA8B,CAAC;AAC5D;AACA;;AAIF,QAAI,QAAQ,aAAa,SACvB,KAAI;KACF,MAAM,EAAE,yBAAa,MAAM,OAAO;AAClC,gBAAS,qCAAqC,WAAW,IAAI,EAC3D,OAAO,QACR,CAAC;AACF,SAAI,QACF,SAAQ,IAAI,UAAU,QAAQ,qBAAqB,CAAC;YAEhD;AACN,aAAQ,IAAI,UAAU,UAAU,iCAAiC,CAAC;;;GAKxE,IAAI,iBAAiB,QAAQ,SAAS,IAAI,aAAa;AAKvD,OADqB,KAAK,QAAQ,aAAa,CAAC,CAAC,KAAK,QAAQ,OAC5C;IAEhB,MAAM,gBACJ,KAAK,QAAQ,WAAW,KAAK,QAAQ,SAAS;IAEhD,MAAM,EAAE,kBAAkB,MAAM,4BADf,KAAK,SAAS,EAAE,gBAAgB,QAAQ,EAGvD,gBACA,KAAK,MACL,eACA,KAAK,QAAQ,cAAc,MAC5B;AACD,qBAAiB;AACjB,QAAI,SAAS;AACX,aAAQ,IAAI,UAAU,QAAQ,kCAAkC,CAAC;AACjE,SAAI,KAAK,QAAQ,WACf,SAAQ,IAAI,UAAU,QAAQ,6BAA6B,CAAC;;AAIhE,QAAI,KAAK,QAAQ,SAAS,CAAC,KAAK,QAAQ,WAAW;AACjD,UAAK,QAAQ,YAAY;AACzB,UAAK,QAAQ,UAAU,KAAK,QAAQ;AACpC,YAAO,KAAK,QAAQ;;;AAMxB,UAAQ,KAAK,QAAoC;AACjD,UAAQ,KAAK,QAAoC;GAGjD,MAAM,EAAE,oBAAS,kBAAQ,sBAAU,UAAU,MAAM,OAAO;GAC1D,IAAI,YAAY,KAAK;AAGrB,OAAI,CAAC,WAAW;IACd,MAAM,iBAAiB;KACrB,KAAK,SAAS,EAAE,aAAa;KAC7B,KAAK,SAAS,EAAE,MAAM;KACtB,KAAK,SAAS,EAAE,OAAO;KACvB;KACA;KACA,KAAK,SAAS,EAAE,gBAAgB,UAAU;KAC3C;AAED,SAAK,MAAM,OAAO,gBAAgB;KAChC,MAAM,eAAe,KAAK,KAAK,KAAK,KAAK;AACzC,SAAI,WAAW,aAAa,CAC1B,KAAI;AAEF,WADc,MAAM,MAAM,aAAa,EAC7B,gBAAgB,EAAE;OAC1B,MAAM,SAAS,MAAMK,WAAS,aAAa;AAC3C,WACE,OAAO,SAAS,oBAAoB,IACpC,OAAO,SAAS,qBAAqB,IACrC,OAAO,SAAS,0BAA0B,EAC1C;AACA,oBAAY;AACZ,YAAI,QACF,SAAQ,IAAI,UAAU,QAAQ,6BAA6B,YAAY,CAAC;AAE1E;;;aAGE;;;AAQd,OAAI,UACF,KAAI;AACF,QAAI,WAAW,UAAU,EAEvB;SADsB,MAAMA,WAAS,UAAU,KACzB,gBAAgB;AACpC,YAAMF,SAAO,UAAU;AACvB,YAAMG,UAAQ,gBAAgB,UAAU;AACxC,UAAI,QACF,SAAQ,IAAI,UAAU,QAAQ,sBAAsB,YAAY,CAAC;;WAGhE;AAEL,WAAMA,UAAQ,gBAAgB,UAAU;AACxC,SAAI,QACF,SAAQ,IAAI,UAAU,QAAQ,wBAAwB,YAAY,CAAC;;AAIvE,SAAK,YAAY;YACV,cAAc;AACrB,YAAQ,IACN,UACE,UACA,mCAAoC,aAAuB,UAC5D,CACF;;AAKL,QAAK,6BAAY,IAAI,MAAM,EAAC,aAAa;AACzC,QAAK,qBAAqB;AAC1B,QAAK,eAAe,gBAAgB,cAAc;AAClD,QAAK,oBAAoB;AACzB,SAAM,kBAAkB,KAAK;AAE7B,WAAQ,IAAI,UAAU,SAAS,2BAA2B,CAAC;AAC3D;WACO,OAAO;AACd,WAAQ,IAAI,UAAU,OAAO,cAAe,MAAgB,UAAU,CAAC;AACvE,OAAI,QACF,SAAQ,MAAO,MAAgB,MAAM;AAEvC;;;AAIJ,SAAQ,KAAK;AACb,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;AAC9C,KAAI,QAAQ;AACV,UAAQ,IAAI,UAAU,CAAC,QAAQ,OAAO,EAAE,qBAAqB,CAAC;AAC9D,UAAQ,IAAI,UAAU,QAAQ,kBAAkB,aAAa,YAAY,CAAC;YACjE,cAAc,GAAG;AAC1B,UAAQ,IAAI,UAAU,CAAC,SAAS,OAAO,EAAE,oBAAoB,CAAC;AAC9D,UAAQ,IAAI,UAAU,QAAQ,aAAa,aAAa,YAAY,CAAC;QAChE;AACL,UAAQ,IAAI,UAAU,CAAC,UAAU,OAAO,EAAE,gCAAgC,CAAC;AAC3E,UAAQ,IAAI,UAAU,QAAQ,cAAc,aAAa,YAAY,YAAY,CAAC;;AAEpF,SAAQ,IAAI,UAAU,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC;EAC9C,CACD,QAAQ,aAAa,kEAAkE,CACvF,OAAO,uBAAuB,8CAA8C,CAC5E,OAAO,qBAAqB,sCAAsC,CAClE,OAAO,mBAAmB,gDAAgD,CAC1E,OAAO,mBAAmB,UAAU,CACpC,OACC,6BACA,8DACD,CACA,OAAO,uBAAuB,mDAAmD,CACjF,OAAO,OAAO,YAAY;CACzB,MAAM,QAAQ,QAAQ;CACtB,MAAM,cAAc,QAAQ;CAC5B,MAAM,UAAU,QAAQ;CACxB,MAAM,SAAS,QAAQ;CACvB,MAAM,cAAc,QAAQ;CAC5B,MAAM,WAAW,QAAQ;AAGzB,KAAI,CAAC,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,UAAU,CAAC,aAAa;AAEjE,QAAM,oBADQ,WAAW,SAAS,UAAU,GAAG,GAAG,OAClB;AAChC;;AAIF,KAAI,CAAC,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,UAAU,CAAC,aAAa;AACjE,UAAQ,IACN,UACE,UACA,2EACD,CACF;AACD,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,SAAS,oBAAoB,CAAC;AACpD,UAAQ,IAAI,UAAU,QAAQ,8BAA8B,CAAC;AAC7D,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,SAAS,qBAAqB,CAAC;AACrD,UAAQ,IAAI,UAAU,QAAQ,iCAAiC,CAAC;AAChE,UAAQ,IAAI,UAAU,QAAQ,yCAAuC,CAAC;AACtE,UAAQ,IAAI,UAAU,QAAQ,6BAA2B,CAAC;AAC1D,UAAQ,IAAI,UAAU,QAAQ,6CAA2C,CAAC;AAC1E,UAAQ,IAAI,UAAU,QAAQ,6BAA2B,CAAC;AAC1D,UAAQ,IAAI,UAAU,QAAQ,uBAAqB,CAAC;AACpD,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,QAAQ,4DAA4D,CAAC;AAC3F,UAAQ,IAAI,UAAU,QAAQ,sDAAsD,CAAC;AACrF,UAAQ,KAAK,EAAE;;CAGjB,MAAMC,iBAA6B;EAAC;EAAa;EAAU;EAA8B;AACzF,KAAI,CAAC,eAAe,SAAS,YAAwB,EAAE;AACrD,UAAQ,IAAI,UAAU,OAAO,4BAA4B,YAAY,GAAG,CAAC;AACzE,UAAQ,IAAI,UAAU,QAAQ,oBAAoB,eAAe,KAAK,KAAK,GAAG,CAAC;AAC/E,UAAQ,KAAK,EAAE;;CAGjB,MAAM,QAAQ,WAAW,SAAS,UAAU,GAAG,GAAG;AAClD,KAAI,aAAa,MAAM,MAAO,IAAI,QAAS,IAAI;AAC7C,UAAQ,IAAI,UAAU,OAAO,6CAA6C,CAAC;AAC3E,UAAQ,KAAK,EAAE;;CAGjB,MAAM,SAAS,SAAS,OAAO,aAAa,SAAS,QAAQ,aAAyB,MAAM;AAE5F,KAAI,OAAO,SAAS;AAClB,UAAQ,IAAI,UAAU,SAAS,OAAO,OAAO,UAAU,CAAC;AACxD,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,SAAS,iBAAiB,CAAC;AACjD,UAAQ,IAAI,UAAU,QAAQ,SAAS,OAAO,MAAM,KAAK,CAAC;AAC1D,UAAQ,IAAI,UAAU,QAAQ,mBAAmB,OAAO,MAAM,cAAc,CAAC;AAC7E,UAAQ,IAAI,UAAU,QAAQ,YAAY,OAAO,MAAM,QAAQ,CAAC;AAChE,UAAQ,IAAI,UAAU,QAAQ,eAAe,OAAO,MAAM,WAAW,CAAC;AACtE,UAAQ,IAAI,UAAU,QAAQ,eAAe,OAAO,MAAM,UAAU,CAAC;QAChE;AACL,UAAQ,IAAI,UAAU,OAAO,UAAU,OAAO,UAAU,CAAC;AACzD,UAAQ,KAAK,EAAE;;EAEjB,CACD,QAAQ,gBAAgB,2CAA2C,CACnE,SAAS,gBAAgB,6CAA6C,CACtE,QAAQ,SAAS,SAAS;CACzB,MAAM,aAAa,OAAO;AAE1B,KAAI,CAAC,YAAY;AACf,UAAQ,IAAI,UAAU,OAAO,mCAAmC,CAAC;AACjE,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,SAAS,SAAS,CAAC;AACzC,UAAQ,IAAI,UAAU,QAAQ,8CAA8C,CAAC;AAC7E,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,QAAQ,qBAAqB,CAAC;AACpD,UAAQ,IAAI,UAAU,QAAQ,mCAAmC,CAAC;AAClE,UAAQ,IAAI,UAAU,QAAQ,6CAA6C,CAAC;AAC5E,UAAQ,IAAI,UAAU,QAAQ,0CAA0C,CAAC;AACzE,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,QAAQ,sDAAsD,CAAC;AACrF,UAAQ,KAAK,EAAE;;CAGjB,MAAM,SAAS,YAAY,WAAW;AAEtC,KAAI,OAAO,WAAW,OAAO,SAAS;AACpC,UAAQ,IAAI,UAAU,SAAS,OAAO,OAAO,UAAU,CAAC;AACxD,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,SAAS,yBAAyB,CAAC;AACzD,UAAQ,IAAI,UAAU,QAAQ,SAAS,OAAO,QAAQ,KAAK,CAAC;AAC5D,UAAQ,IAAI,UAAU,QAAQ,mBAAmB,OAAO,QAAQ,cAAc,CAAC;AAC/E,UAAQ,IAAI,UAAU,QAAQ,YAAY,OAAO,QAAQ,QAAQ,CAAC;AAClE,UAAQ,IAAI,UAAU,QAAQ,eAAe,OAAO,QAAQ,WAAW,CAAC;AAExE,MAAI,OAAO,iBAAiB,OAAO,cAAc,SAAS,GAAG;AAC3D,WAAQ,KAAK;AACb,WAAQ,IAAI,UAAU,UAAU,0CAA0C,CAAC;AAC3E,QAAK,MAAM,WAAW,OAAO,eAAe;AAC1C,YAAQ,IAAI,UAAU,QAAQ,KAAK,QAAQ,YAAY,GAAG,CAAC;AAC3D,YAAQ,IAAI,UAAU,QAAQ,OAAO,QAAQ,MAAM,KAAK,QAAQ,QAAQ,CAAC;;;AAG7E,UAAQ,KAAK;AACb,UAAQ,IAAI,UAAU,QAAQ,0DAA0D,CAAC;QACpF;AACL,UAAQ,IAAI,UAAU,OAAO,UAAU,OAAO,UAAU,CAAC;AACzD,UAAQ,KAAK,EAAE;;EAEjB,CACD,QAAQ,eAAe,0CAA0C,CACjE,aAAa;AACZ,kBAAiB;EACjB,CACD,KAAK,CACL,OAAO,QAAe;AACrB,SAAQ,MAAM,IAAI;AAClB,SAAQ,KAAK,EAAE;EACf"}
1
+ {"version":3,"file":"cli.mjs","names":["IS_WINDOWS","newModel: CustomModel","index: number","updatedModels: { oldId: string; newId: string; displayName: string }[]","patches: Patch[]","flag: FilterFlag | undefined","allowedFlags: FilterFlag[]","unlink","metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[]","readlink","symlink","validProviders: Provider[]"],"sources":["../src/websearch-external.ts","../src/websearch-native.ts","../src/websearch-patch.ts","../src/model-manager.ts","../src/cli.ts"],"sourcesContent":["/**\n * WebSearch External Providers Mode (--websearch)\n *\n * Priority: Smithery Exa > Google PSE > Tavily > Serper > Brave > SearXNG > DuckDuckGo\n */\n\nexport function generateSearchProxyServerCode(): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (External Providers Mode)\n// Priority: Smithery Exa > Google PSE > Tavily > Serper > Brave > SearXNG > DuckDuckGo\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = 'https://api.factory.ai';\n\nfunction log() { if (DEBUG) console.error.apply(console, ['[websearch]'].concat(Array.from(arguments))); }\n\n// === External Search Providers ===\n\nasync function searchSmitheryExa(query, numResults) {\n const apiKey = process.env.SMITHERY_API_KEY;\n const profile = process.env.SMITHERY_PROFILE;\n if (!apiKey || !profile) return null;\n try {\n const serverUrl = 'https://server.smithery.ai/exa/mcp?api_key=' + encodeURIComponent(apiKey) + '&profile=' + encodeURIComponent(profile);\n const requestBody = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'web_search_exa', arguments: { query: query, numResults: numResults } } });\n const bodyStr = requestBody.replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + serverUrl + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const response = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 30000 }));\n if (response.result && response.result.content) {\n const textContent = response.result.content.find(function(c) { return c.type === 'text'; });\n if (textContent && textContent.text) {\n const results = JSON.parse(textContent.text);\n if (Array.isArray(results) && results.length > 0) {\n return results.slice(0, numResults).map(function(item) {\n return {\n title: item.title || '', url: item.url || '',\n content: item.text || item.snippet || (item.highlights ? item.highlights.join(' ') : '') || ''\n };\n });\n }\n }\n }\n } catch (e) { log('Smithery failed:', e.message); }\n return null;\n}\n\nasync function searchGooglePSE(query, numResults) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = 'https://www.googleapis.com/customsearch/v1?key=' + apiKey + '&cx=' + cx + '&q=' + encodeURIComponent(query) + '&num=' + Math.min(numResults, 10);\n const data = JSON.parse(execSync('curl -s \"' + url + '\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.error) return null;\n return (data.items || []).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n } catch (e) { log('Google PSE failed:', e.message); }\n return null;\n}\n\nasync function searchTavily(query, numResults) {\n const apiKey = process.env.TAVILY_API_KEY;\n if (!apiKey) return null;\n try {\n const bodyStr = JSON.stringify({\n api_key: apiKey,\n query: query,\n max_results: numResults,\n search_depth: 'basic',\n include_answer: false,\n include_images: false,\n include_raw_content: false\n }).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s \"https://api.tavily.com/search\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data && Array.isArray(data.results) && data.results.length > 0) {\n return data.results.slice(0, numResults).map(function(item) {\n return { title: item.title || '', url: item.url || '', content: item.content || item.snippet || item.raw_content || '' };\n });\n }\n } catch (e) { log('Tavily failed:', e.message); }\n return null;\n}\n\nasync function searchSerper(query, numResults) {\n const apiKey = process.env.SERPER_API_KEY;\n if (!apiKey) return null;\n try {\n const bodyStr = JSON.stringify({ q: query, num: numResults }).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s \"https://google.serper.dev/search\" -H \"X-API-KEY: ' + apiKey + '\" -H \"Content-Type: application/json\" -d \\\\'' + bodyStr + \"\\\\'\";\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.organic && data.organic.length > 0) {\n return data.organic.slice(0, numResults).map(function(item) { return { title: item.title, url: item.link, content: item.snippet || '' }; });\n }\n } catch (e) { log('Serper failed:', e.message); }\n return null;\n}\n\nasync function searchBrave(query, numResults) {\n const apiKey = process.env.BRAVE_API_KEY;\n if (!apiKey) return null;\n try {\n const url = 'https://api.search.brave.com/res/v1/web/search?q=' + encodeURIComponent(query) + '&count=' + numResults;\n const curlCmd = 'curl -s \"' + url + '\" -H \"Accept: application/json\" -H \"X-Subscription-Token: ' + apiKey + '\"';\n const data = JSON.parse(execSync(curlCmd, { encoding: 'utf-8', timeout: 15000 }));\n if (data.web && data.web.results && data.web.results.length > 0) {\n return data.web.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.description || '' }; });\n }\n } catch (e) { log('Brave failed:', e.message); }\n return null;\n}\n\nasync function searchSearXNG(query, numResults) {\n const searxngUrl = process.env.SEARXNG_URL;\n if (!searxngUrl) return null;\n try {\n const url = searxngUrl + '/search?q=' + encodeURIComponent(query) + '&format=json&engines=google,bing,duckduckgo';\n const data = JSON.parse(execSync('curl -s \"' + url + '\" -H \"Accept: application/json\"', { encoding: 'utf-8', timeout: 15000 }));\n if (data.results && data.results.length > 0) {\n return data.results.slice(0, numResults).map(function(item) { return { title: item.title, url: item.url, content: item.content || '' }; });\n }\n } catch (e) { log('SearXNG failed:', e.message); }\n return null;\n}\n\nasync function searchDuckDuckGo(query, numResults) {\n try {\n const apiUrl = 'https://api.duckduckgo.com/?q=' + encodeURIComponent(query) + '&format=json&no_html=1&skip_disambig=1';\n const data = JSON.parse(execSync('curl -s \"' + apiUrl + '\" -H \"User-Agent: Mozilla/5.0\"', { encoding: 'utf-8', timeout: 15000 }));\n const results = [];\n if (data.Abstract && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.Abstract });\n }\n var topics = data.RelatedTopics || [];\n for (var i = 0; i < topics.length && results.length < numResults; i++) {\n var topic = topics[i];\n if (topic.Text && topic.FirstURL) results.push({ title: topic.Text.substring(0, 100), url: topic.FirstURL, content: topic.Text });\n if (topic.Topics) {\n for (var j = 0; j < topic.Topics.length && results.length < numResults; j++) {\n var st = topic.Topics[j];\n if (st.Text && st.FirstURL) results.push({ title: st.Text.substring(0, 100), url: st.FirstURL, content: st.Text });\n }\n }\n }\n return results.length > 0 ? results : null;\n } catch (e) { log('DuckDuckGo failed:', e.message); }\n return null;\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n // Priority: Smithery > Google PSE > Tavily > Serper > Brave > SearXNG > DuckDuckGo\n var results = await searchSmitheryExa(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'smithery-exa' };\n \n results = await searchGooglePSE(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'google-pse' };\n \n results = await searchTavily(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'tavily' };\n \n results = await searchSerper(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'serper' };\n \n results = await searchBrave(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'brave' };\n \n results = await searchSearXNG(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'searxng' };\n \n results = await searchDuckDuckGo(query, numResults);\n if (results && results.length > 0) return { results: results, source: 'duckduckgo' };\n \n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'external-providers' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Proxy other requests\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('External providers proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n\nexport function generateExternalSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n const code = generateSearchProxyServerCode();\n return code.replace(\n \"const FACTORY_API = 'https://api.factory.ai';\",\n `const FACTORY_API = '${factoryApiUrl}';`,\n );\n}\n","/**\n * WebSearch Native Provider Mode (--websearch-proxy)\n *\n * Uses model's native websearch based on ~/.factory/settings.json configuration\n * Requires proxy plugin to handle format conversion:\n * - Anthropic provider: anthropic4droid plugin\n * - OpenAI provider: openai4droid plugin (adds CODEX_INSTRUCTIONS)\n *\n * Supported providers:\n * - Anthropic: web_search_20250305 server tool, results in web_search_tool_result\n * - OpenAI: web_search tool, results in message.content[].annotations[] as url_citation\n */\n\nexport function generateNativeSearchProxyServer(\n factoryApiUrl: string = \"https://api.factory.ai\",\n): string {\n return `#!/usr/bin/env node\n// Droid WebSearch Proxy Server (Native Provider Mode)\n// Reads ~/.factory/settings.json for model configuration\n// Requires proxy plugin (anthropic4droid) to handle format conversion\n\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\nconst os = require('os');\n\nconst DEBUG = process.env.DROID_SEARCH_DEBUG === '1';\nconst PORT = parseInt(process.env.SEARCH_PROXY_PORT || '0');\nconst FACTORY_API = '${factoryApiUrl}';\n\nfunction log(...args) { if (DEBUG) console.error('[websearch]', ...args); }\n\n// === Settings Configuration ===\n\nlet cachedSettings = null;\nlet settingsLastModified = 0;\n\nfunction getFactorySettings() {\n const settingsPath = path.join(os.homedir(), '.factory', 'settings.json');\n try {\n const stats = fs.statSync(settingsPath);\n if (cachedSettings && stats.mtimeMs === settingsLastModified) return cachedSettings;\n cachedSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n settingsLastModified = stats.mtimeMs;\n return cachedSettings;\n } catch (e) {\n log('Failed to load settings.json:', e.message);\n return null;\n }\n}\n\nfunction getCurrentModelConfig() {\n const settings = getFactorySettings();\n if (!settings) return null;\n \n const currentModelId = settings.sessionDefaultSettings?.model;\n if (!currentModelId) return null;\n \n const customModels = settings.customModels || [];\n const modelConfig = customModels.find(m => m.id === currentModelId);\n \n if (modelConfig) {\n log('Model:', modelConfig.displayName, '| Provider:', modelConfig.provider);\n return modelConfig;\n }\n \n if (!currentModelId.startsWith('custom:')) return null;\n log('Model not found:', currentModelId);\n return null;\n}\n\n// === Native Provider WebSearch ===\n\nasync function searchAnthropicNative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n const requestBody = {\n model: model,\n max_tokens: 4096,\n stream: false,\n system: 'You are a web search assistant. Use the web_search tool to find relevant information and return the results.',\n tools: [{ type: 'web_search_20250305', name: 'web_search', max_uses: 1 }],\n tool_choice: { type: 'tool', name: 'web_search' },\n messages: [{ role: 'user', content: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.' }]\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/v1/messages')) endpoint = endpoint.replace(/\\\\/$/, '') + '/v1/messages';\n \n log('Anthropic search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"anthropic-version: 2023-06-01\" -H \"x-api-key: ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n const results = [];\n for (const block of (response.content || [])) {\n if (block.type === 'web_search_tool_result') {\n for (const result of (block.content || [])) {\n if (result.type === 'web_search_result') {\n results.push({\n title: result.title || '',\n url: result.url || '',\n content: result.snippet || result.page_content || ''\n });\n }\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('Anthropic error:', e.message);\n return null;\n }\n}\n\nasync function searchOpenAINative(query, numResults, modelConfig) {\n const { baseUrl, apiKey, model } = modelConfig;\n \n try {\n // Note: instructions will be added by openai4droid proxy plugin\n const requestBody = {\n model: model,\n stream: false,\n tools: [{ type: 'web_search' }],\n tool_choice: 'required',\n input: 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.'\n };\n \n let endpoint = baseUrl;\n if (!endpoint.endsWith('/responses')) endpoint = endpoint.replace(/\\\\/$/, '') + '/responses';\n \n log('OpenAI search:', query, '→', endpoint);\n \n const bodyStr = JSON.stringify(requestBody).replace(/'/g, \"'\\\\\\\\''\");\n const curlCmd = 'curl -s -X POST \"' + endpoint + '\" -H \"Content-Type: application/json\" -H \"Authorization: Bearer ' + apiKey + '\" -d \\\\'' + bodyStr + \"\\\\'\";\n const responseStr = execSync(curlCmd, { encoding: 'utf-8', timeout: 60000 });\n \n let response;\n try { response = JSON.parse(responseStr); } catch { return null; }\n if (response.error) { log('API error:', response.error.message); return null; }\n \n // Extract results from url_citation annotations in message output\n const results = [];\n for (const item of (response.output || [])) {\n if (item.type === 'message' && Array.isArray(item.content)) {\n for (const content of item.content) {\n if (content.type === 'output_text' && Array.isArray(content.annotations)) {\n for (const annotation of content.annotations) {\n if (annotation.type === 'url_citation' && annotation.url) {\n results.push({\n title: annotation.title || '',\n url: annotation.url || '',\n content: annotation.title || ''\n });\n }\n }\n }\n }\n }\n }\n \n log('Results:', results.length);\n return results.length > 0 ? results.slice(0, numResults) : null;\n } catch (e) {\n log('OpenAI error:', e.message);\n return null;\n }\n}\n\nasync function search(query, numResults) {\n numResults = numResults || 10;\n log('Search:', query);\n \n const modelConfig = getCurrentModelConfig();\n if (!modelConfig) {\n log('No custom model configured');\n return { results: [], source: 'none' };\n }\n \n const provider = modelConfig.provider;\n let results = null;\n \n if (provider === 'anthropic') results = await searchAnthropicNative(query, numResults, modelConfig);\n else if (provider === 'openai') results = await searchOpenAINative(query, numResults, modelConfig);\n else log('Unsupported provider:', provider);\n \n if (results && results.length > 0) return { results: results, source: 'native-' + provider };\n return { results: [], source: 'none' };\n}\n\n// === HTTP Proxy Server ===\n\nconst server = http.createServer(async (req, res) => {\n const url = new URL(req.url, 'http://' + req.headers.host);\n\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok', mode: 'native-provider' }));\n return;\n }\n\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', function(c) { body += c; });\n req.on('end', async function() {\n try {\n const parsed = JSON.parse(body);\n const result = await search(parsed.query, parsed.numResults || 10);\n log('Results:', result.results.length, 'from', result.source);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results: result.results }));\n } catch (e) {\n log('Search error:', e.message);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n\n // Standalone mode: mock non-LLM APIs\n if (process.env.STANDALONE_MODE === '1') {\n const pathname = url.pathname;\n const isCoreLLMApi = pathname.startsWith('/api/llm/a/') || pathname.startsWith('/api/llm/o/');\n\n if (!isCoreLLMApi) {\n if (pathname === '/api/sessions/create') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ id: 'local-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10) }));\n return;\n }\n if (pathname === '/api/cli/whoami') {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({}));\n return;\n }\n }\n\n // Simple proxy - no SSE transformation (handled by proxy plugin)\n log('Proxy:', req.method, url.pathname);\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyModule = proxyUrl.protocol === 'https:' ? https : http;\n const proxyReq = proxyModule.request(proxyUrl, {\n method: req.method,\n headers: Object.assign({}, req.headers, { host: proxyUrl.host })\n }, function(proxyRes) {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', function(e) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Proxy failed: ' + e.message }));\n });\n\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n});\n\nserver.listen(PORT, '127.0.0.1', function() {\n const actualPort = server.address().port;\n const portFile = process.env.SEARCH_PROXY_PORT_FILE;\n if (portFile) fs.writeFileSync(portFile, String(actualPort));\n console.log('PORT=' + actualPort);\n log('Native provider proxy on port', actualPort);\n});\n\nprocess.on('SIGTERM', function() { server.close(); process.exit(0); });\nprocess.on('SIGINT', function() { server.close(); process.exit(0); });\n`;\n}\n","/**\n * WebSearch Patch Generator\n *\n * Two modes:\n * - --websearch: External providers (Smithery, Google PSE, Tavily, Serper, Brave, SearXNG, DuckDuckGo)\n * - --websearch-proxy: Native provider via proxy plugin (reads ~/.factory/settings.json)\n */\n\nimport type { Patch } from \"./patcher.ts\";\nimport { writeFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { platform } from \"node:os\";\nimport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nimport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\nconst IS_WINDOWS = platform() === \"win32\";\n\n// Re-export for backward compatibility\nexport {\n generateSearchProxyServerCode,\n generateExternalSearchProxyServer,\n} from \"./websearch-external.ts\";\nexport { generateNativeSearchProxyServer } from \"./websearch-native.ts\";\n\n/**\n * Generate wrapper script for standalone proxy mode\n */\nfunction generateWrapperScript(droidPath: string, proxyScriptPath: string): string {\n return `#!/bin/bash\n# Droid with WebSearch Proxy\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPORT_FILE=\"/tmp/droid-search-proxy-$$.port\"\n\nstart_proxy() {\n SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" &\n PROXY_PID=$!\n for i in {1..20}; do\n if [ -f \"$PORT_FILE\" ]; then\n PORT=$(cat \"$PORT_FILE\")\n if curl -s \"http://127.0.0.1:$PORT/health\" > /dev/null 2>&1; then\n echo \"[websearch] Proxy started on port $PORT\"\n return 0\n fi\n fi\n sleep 0.2\n done\n echo \"[websearch] Failed to start proxy\"\n kill $PROXY_PID 2>/dev/null\n return 1\n}\n\ncleanup() {\n [ -n \"$PROXY_PID\" ] && kill $PROXY_PID 2>/dev/null\n [ -f \"$PORT_FILE\" ] && rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT\n\nif ! start_proxy; then exit 1; fi\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$PORT\"\nexec \"$DROID_BIN\" \"$@\"\n`;\n}\n\n/**\n * Generate WebSearch Patch (legacy binary patch approach)\n */\nexport function generateWebSearchPatch(): Patch | null {\n const originalUrl = \"https://api.factory.ai\";\n const localUrl = \"http://127.0.0.1:23119\";\n\n if (localUrl.length > originalUrl.length) {\n console.error(`[websearch] Local URL too long: ${localUrl.length} > ${originalUrl.length}`);\n return null;\n }\n\n const paddedUrl = localUrl.padEnd(originalUrl.length, \" \");\n return {\n name: \"webSearch\",\n description: `Replace API URL with local proxy (${localUrl})`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n };\n}\n\n/**\n * Create WebSearch proxy files (legacy)\n */\nexport async function createWebSearchProxyFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ proxyScript: string; wrapperScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-search-proxy.js`);\n const wrapperScriptPath = join(outputDir, `${aliasName}-with-search`);\n\n await writeFile(proxyScriptPath, generateSearchProxyServerCode());\n console.log(`[*] Created search proxy: ${proxyScriptPath}`);\n\n await writeFile(wrapperScriptPath, generateWrapperScript(droidPath, proxyScriptPath));\n await chmod(wrapperScriptPath, 0o755);\n console.log(`[*] Created wrapper script: ${wrapperScriptPath}`);\n\n return { proxyScript: proxyScriptPath, wrapperScript: wrapperScriptPath };\n}\n\n/**\n * Get proxy server code (for export)\n */\nexport function getSearchProxyCode(): string {\n return generateSearchProxyServerCode();\n}\n\n/**\n * Generate unified wrapper script (Unix bash)\n */\nfunction generateUnifiedWrapper(\n droidPath: string,\n proxyScriptPath: string,\n standalone: boolean = false,\n): string {\n const standaloneEnv = standalone ? \"STANDALONE_MODE=1 \" : \"\";\n return `#!/bin/bash\n# Droid with WebSearch\n# Auto-generated by droid-patch --websearch\n\nPROXY_SCRIPT=\"${proxyScriptPath}\"\nDROID_BIN=\"${droidPath}\"\nPROXY_PID=\"\"\nPORT_FILE=\"/tmp/droid-websearch-$$.port\"\nSTANDALONE=\"${standalone ? \"1\" : \"0\"}\"\n\nshould_passthrough() {\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then break; fi\n case \"$arg\" in --help|-h|--version|-V) return 0 ;; esac\n done\n local end_opts=0\n for arg in \"$@\"; do\n if [ \"$arg\" = \"--\" ]; then end_opts=1; continue; fi\n if [ \"$end_opts\" -eq 0 ] && [[ \"$arg\" == -* ]]; then continue; fi\n case \"$arg\" in help|version|completion|completions|exec|plugin) return 0 ;; esac\n break\n done\n return 1\n}\n\nif should_passthrough \"$@\"; then exec \"$DROID_BIN\" \"$@\"; fi\n\ncleanup() {\n if [ -n \"$PROXY_PID\" ] && kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Stopping proxy (PID: $PROXY_PID)\" >&2\n kill \"$PROXY_PID\" 2>/dev/null\n wait \"$PROXY_PID\" 2>/dev/null\n fi\n rm -f \"$PORT_FILE\"\n}\ntrap cleanup EXIT INT TERM\n\n[ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Starting proxy...\" >&2\n[ \"$STANDALONE\" = \"1\" ] && [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Standalone mode enabled\" >&2\n\nif [ -n \"$DROID_SEARCH_DEBUG\" ]; then\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" 2>&1 &\nelse\n ${standaloneEnv}SEARCH_PROXY_PORT=0 SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" >/dev/null 2>&1 &\nfi\nPROXY_PID=$!\n\nfor i in {1..50}; do\n if ! kill -0 \"$PROXY_PID\" 2>/dev/null; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy process died\" >&2\n break\n fi\n if [ -f \"$PORT_FILE\" ]; then\n ACTUAL_PORT=$(cat \"$PORT_FILE\" 2>/dev/null)\n if [ -n \"$ACTUAL_PORT\" ] && curl -s \"http://127.0.0.1:$ACTUAL_PORT/health\" > /dev/null 2>&1; then\n [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy ready on port $ACTUAL_PORT (PID: $PROXY_PID)\" >&2\n break\n fi\n fi\n sleep 0.1\ndone\n\nif [ ! -f \"$PORT_FILE\" ] || [ -z \"$(cat \"$PORT_FILE\" 2>/dev/null)\" ]; then\n echo \"[websearch] Failed to start proxy, running without websearch\" >&2\n cleanup\n exec \"$DROID_BIN\" \"$@\"\nfi\n\nACTUAL_PORT=$(cat \"$PORT_FILE\")\nrm -f \"$PORT_FILE\"\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$ACTUAL_PORT\"\n\"$DROID_BIN\" \"$@\"\nDROID_EXIT_CODE=$?\nexit $DROID_EXIT_CODE\n`;\n}\n\n/**\n * Generate Windows wrapper script (.cmd batch file)\n */\nfunction generateWindowsWrapper(\n droidPath: string,\n proxyScriptPath: string,\n standalone: boolean = false,\n): string {\n const standaloneEnv = standalone ? \"set STANDALONE_MODE=1\\r\\n\" : \"\";\n return `@echo off\nsetlocal enabledelayedexpansion\nREM Droid with WebSearch\nREM Auto-generated by droid-patch --websearch\n\nset \"PROXY_SCRIPT=${proxyScriptPath}\"\nset \"DROID_BIN=${droidPath}\"\nset \"PORT_FILE=%TEMP%\\\\droid-websearch-%RANDOM%%RANDOM%.port\"\n\nREM Check for passthrough commands\nset \"PASSTHROUGH=0\"\nfor %%a in (%*) do (\n if \"%%a\"==\"--help\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"-h\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"--version\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"-V\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"help\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"version\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"completion\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"completions\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"exec\" set \"PASSTHROUGH=1\"\n if \"%%a\"==\"plugin\" set \"PASSTHROUGH=1\"\n)\nif \"%PASSTHROUGH%\"==\"1\" (\n \"%DROID_BIN%\" %*\n exit /b %ERRORLEVEL%\n)\n\nREM Start proxy server\n${standaloneEnv}set \"SEARCH_PROXY_PORT=0\"\nset \"SEARCH_PROXY_PORT_FILE=%PORT_FILE%\"\n\nif defined DROID_SEARCH_DEBUG (\n echo [websearch] Starting proxy... 1>&2\n start /b node \"%PROXY_SCRIPT%\"\n) else (\n start /b node \"%PROXY_SCRIPT%\" >nul 2>&1\n)\n\nREM Wait for port file\nset \"RETRY=0\"\n:wait_loop\nif %RETRY% GEQ 50 goto :proxy_failed\nset /a RETRY+=1\ntimeout /t 1 /nobreak >nul 2>&1\nif not exist \"%PORT_FILE%\" goto :wait_loop\n\nREM Read port and check health\nset /p ACTUAL_PORT=<\"%PORT_FILE%\"\nif not defined ACTUAL_PORT goto :wait_loop\n\nREM Simple health check using curl or PowerShell\nwhere curl >nul 2>&1\nif %ERRORLEVEL%==0 (\n curl -s \"http://127.0.0.1:%ACTUAL_PORT%/health\" >nul 2>&1\n if %ERRORLEVEL%==0 goto :proxy_ready\n) else (\n powershell -Command \"try { Invoke-WebRequest -Uri 'http://127.0.0.1:%ACTUAL_PORT%/health' -UseBasicParsing -TimeoutSec 2 | Out-Null; exit 0 } catch { exit 1 }\" >nul 2>&1\n if %ERRORLEVEL%==0 goto :proxy_ready\n)\ngoto :wait_loop\n\n:proxy_failed\necho [websearch] Failed to start proxy, running without websearch 1>&2\ndel \"%PORT_FILE%\" 2>nul\n\"%DROID_BIN%\" %*\nexit /b %ERRORLEVEL%\n\n:proxy_ready\nif defined DROID_SEARCH_DEBUG echo [websearch] Proxy ready on port %ACTUAL_PORT% 1>&2\ndel \"%PORT_FILE%\" 2>nul\n\nset \"FACTORY_API_BASE_URL_OVERRIDE=http://127.0.0.1:%ACTUAL_PORT%\"\n\"%DROID_BIN%\" %*\nset \"DROID_EXIT_CODE=%ERRORLEVEL%\"\n\nREM Cleanup: kill node processes started by this script\nREM Note: Windows doesn't have easy process tree tracking, proxy will exit when parent exits\nexit /b %DROID_EXIT_CODE%\n`;\n}\n\n/**\n * Create unified WebSearch files\n *\n * @param outputDir - Directory to write files to\n * @param droidPath - Path to droid binary\n * @param aliasName - Alias name for the wrapper\n * @param apiBase - Custom API base URL for proxy to forward requests to\n * @param standalone - Standalone mode: mock non-LLM Factory APIs\n * @param useNativeProvider - Use native provider websearch (--websearch-proxy mode)\n */\nexport async function createWebSearchUnifiedFiles(\n outputDir: string,\n droidPath: string,\n aliasName: string,\n apiBase?: string,\n standalone: boolean = false,\n useNativeProvider: boolean = false,\n): Promise<{ wrapperScript: string; preloadScript: string }> {\n if (!existsSync(outputDir)) {\n await mkdir(outputDir, { recursive: true });\n }\n\n const proxyScriptPath = join(outputDir, `${aliasName}-proxy.js`);\n // Windows uses .cmd extension, Unix has no extension\n const wrapperScriptPath = IS_WINDOWS\n ? join(outputDir, `${aliasName}.cmd`)\n : join(outputDir, aliasName);\n\n const factoryApiUrl = apiBase || \"https://api.factory.ai\";\n const proxyCode = useNativeProvider\n ? generateNativeSearchProxyServer(factoryApiUrl)\n : generateExternalSearchProxyServer(factoryApiUrl);\n\n await writeFile(proxyScriptPath, proxyCode);\n console.log(`[*] Created proxy script: ${proxyScriptPath}`);\n console.log(\n `[*] Mode: ${useNativeProvider ? \"native provider (requires proxy plugin)\" : \"external providers\"}`,\n );\n\n // Generate platform-specific wrapper\n const wrapperCode = IS_WINDOWS\n ? generateWindowsWrapper(droidPath, proxyScriptPath, standalone)\n : generateUnifiedWrapper(droidPath, proxyScriptPath, standalone);\n\n await writeFile(wrapperScriptPath, wrapperCode);\n if (!IS_WINDOWS) {\n await chmod(wrapperScriptPath, 0o755);\n }\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n if (standalone) {\n console.log(`[*] Standalone mode enabled`);\n }\n\n return {\n wrapperScript: wrapperScriptPath,\n preloadScript: proxyScriptPath,\n };\n}\n\n// === Legacy Preload Mode (kept for compatibility) ===\n\nfunction generatePreloadScript(): string {\n return `// Droid WebSearch Preload Script\nconst http = require('http');\nconst https = require('https');\nconst { execSync } = require('child_process');\n\nconst PORT = process.env.DROID_SEARCH_PORT || 23119;\nconst FACTORY_API = 'https://api.factory.ai';\n\nasync function searchGooglePSE(query, num) {\n const apiKey = process.env.GOOGLE_PSE_API_KEY;\n const cx = process.env.GOOGLE_PSE_CX;\n if (!apiKey || !cx) return null;\n try {\n const url = \\`https://www.googleapis.com/customsearch/v1?key=\\${apiKey}&cx=\\${cx}&q=\\${encodeURIComponent(query)}&num=\\${Math.min(num, 10)}\\`;\n const res = await fetch(url);\n const data = await res.json();\n if (data.error) return null;\n return (data.items || []).map(item => ({ title: item.title, url: item.link, content: item.snippet || '' }));\n } catch { return null; }\n}\n\nfunction searchDuckDuckGo(query, num) {\n try {\n const url = \\`https://api.duckduckgo.com/?q=\\${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1\\`;\n const output = execSync(\\`curl -s \"\\${url}\"\\`, { encoding: 'utf8', timeout: 10000 });\n const data = JSON.parse(output);\n const results = [];\n if (data.AbstractText && data.AbstractURL) {\n results.push({ title: data.Heading || query, url: data.AbstractURL, content: data.AbstractText });\n }\n for (const t of (data.RelatedTopics || [])) {\n if (results.length >= num) break;\n if (t.Text && t.FirstURL) results.push({ title: t.Text.split(' - ')[0], url: t.FirstURL, content: t.Text });\n if (t.Topics) {\n for (const sub of t.Topics) {\n if (results.length >= num) break;\n if (sub.Text && sub.FirstURL) results.push({ title: sub.Text.split(' - ')[0], url: sub.FirstURL, content: sub.Text });\n }\n }\n }\n return results;\n } catch { return []; }\n}\n\nasync function search(query, num) {\n const googleResults = await searchGooglePSE(query, num);\n if (googleResults?.length > 0) return googleResults;\n return searchDuckDuckGo(query, num);\n}\n\nfunction isPortInUse(port) {\n try { execSync(\\`curl -s http://127.0.0.1:\\${port}/health\\`, { timeout: 1000 }); return true; } catch { return false; }\n}\n\nif (!isPortInUse(PORT)) {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url, \\`http://\\${req.headers.host}\\`);\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n if (url.pathname === '/api/tools/exa/search' && req.method === 'POST') {\n let body = '';\n req.on('data', c => body += c);\n req.on('end', async () => {\n try {\n const { query, numResults } = JSON.parse(body);\n const results = await search(query, numResults || 10);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results }));\n } catch (e) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(e), results: [] }));\n }\n });\n return;\n }\n const proxyUrl = new URL(FACTORY_API + url.pathname + url.search);\n const proxyReq = https.request(proxyUrl, { method: req.method, headers: { ...req.headers, host: proxyUrl.host } }, proxyRes => {\n res.writeHead(proxyRes.statusCode, proxyRes.headers);\n proxyRes.pipe(res);\n });\n proxyReq.on('error', () => { res.writeHead(502); res.end(); });\n if (req.method !== 'GET' && req.method !== 'HEAD') req.pipe(proxyReq);\n else proxyReq.end();\n });\n server.listen(PORT, '127.0.0.1');\n}\n`;\n}\n\nfunction generateBunfigToml(preloadScriptPath: string): string {\n return `preload = [\"${preloadScriptPath}\"]`;\n}\n\nfunction generatePreloadWrapperScript(droidPath: string, bunfigDir: string): string {\n return `#!/bin/bash\ncd \"${bunfigDir}\"\nexec \"${droidPath}\" --cwd \"$(pwd)\" \"$@\"\n`;\n}\n\nexport async function createWebSearchPreloadFiles(\n droidDir: string,\n droidPath: string,\n aliasName: string,\n): Promise<{ preloadScript: string; bunfigPath: string; wrapperScript: string }> {\n if (!existsSync(droidDir)) {\n await mkdir(droidDir, { recursive: true });\n }\n\n const preloadScriptPath = join(droidDir, `${aliasName}-search-preload.js`);\n const bunfigPath = join(droidDir, \"bunfig.toml\");\n const wrapperScriptPath = join(droidDir, aliasName);\n\n await writeFile(preloadScriptPath, generatePreloadScript());\n await writeFile(bunfigPath, generateBunfigToml(preloadScriptPath));\n await writeFile(wrapperScriptPath, generatePreloadWrapperScript(droidPath, droidDir));\n await chmod(wrapperScriptPath, 0o755);\n\n console.log(`[*] Created preload: ${preloadScriptPath}`);\n console.log(`[*] Created bunfig: ${bunfigPath}`);\n console.log(`[*] Created wrapper: ${wrapperScriptPath}`);\n\n return { preloadScript: preloadScriptPath, bunfigPath, wrapperScript: wrapperScriptPath };\n}\n\nexport function getPreloadScriptCode(): string {\n return generatePreloadScript();\n}\n","/**\n * Custom Model Manager\n * Manages custom models in ~/.factory/settings.json\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { styleText } from \"node:util\";\nimport { createInterface } from \"node:readline/promises\";\n\nconst FACTORY_DIR = join(homedir(), \".factory\");\nconst SETTINGS_PATH = join(FACTORY_DIR, \"settings.json\");\n\nexport type Provider = \"anthropic\" | \"openai\" | \"generic-chat-completion-api\";\n\nexport interface CustomModel {\n model: string;\n id: string;\n baseUrl: string;\n apiKey: string;\n displayName: string;\n provider: Provider;\n index: number;\n noImageSupport: boolean;\n}\n\ninterface FactorySettings {\n customModels?: CustomModel[];\n sessionDefaultSettings?: {\n model?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n/**\n * Generate model ID from displayName and index\n * Format: custom:{DisplayName}-{index} where spaces are replaced with -\n * This matches droid's buildCustomModelId function\n */\nexport function generateModelId(displayName: string, index: number): string {\n const normalized = displayName.trim().replace(/\\s+/g, \"-\");\n return `custom:${normalized}-${index}`;\n}\n\n/**\n * Load settings.json\n */\nexport function loadSettings(): FactorySettings {\n if (!existsSync(SETTINGS_PATH)) {\n return {};\n }\n try {\n const content = readFileSync(SETTINGS_PATH, \"utf-8\");\n return JSON.parse(content) as FactorySettings;\n } catch {\n return {};\n }\n}\n\n/**\n * Save settings.json\n */\nexport function saveSettings(settings: FactorySettings): void {\n if (!existsSync(FACTORY_DIR)) {\n mkdirSync(FACTORY_DIR, { recursive: true });\n }\n writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));\n}\n\n/**\n * Rebuild all model IDs and indexes based on their array position\n * This is necessary because droid uses array index as part of the ID\n */\nfunction rebuildModelIds(models: CustomModel[]): CustomModel[] {\n return models.map((model, index) => ({\n ...model,\n id: generateModelId(model.displayName, index),\n index,\n noImageSupport: model.noImageSupport ?? false,\n }));\n}\n\n/**\n * Update default model reference if needed\n */\nfunction updateDefaultModelRef(settings: FactorySettings, oldId: string, newId: string): void {\n if (settings.sessionDefaultSettings?.model === oldId) {\n settings.sessionDefaultSettings.model = newId;\n }\n}\n\n/**\n * Add a custom model at specified index (or end if not specified)\n */\nexport function addModel(\n modelName: string,\n displayName: string,\n baseUrl: string,\n apiKey: string,\n provider: Provider,\n insertIndex?: number,\n): { success: boolean; model: CustomModel; message: string } {\n const settings = loadSettings();\n\n if (!settings.customModels) {\n settings.customModels = [];\n }\n\n // Determine insert position\n const actualIndex = insertIndex ?? settings.customModels.length;\n if (actualIndex < 0 || actualIndex > settings.customModels.length) {\n return {\n success: false,\n model: {} as CustomModel,\n message: `Invalid index ${actualIndex}. Valid range: 0-${settings.customModels.length}`,\n };\n }\n\n const newModel: CustomModel = {\n model: modelName,\n id: \"\", // Will be set by rebuildModelIds\n baseUrl,\n apiKey,\n displayName,\n provider,\n index: 0, // Will be set by rebuildModelIds\n noImageSupport: false,\n };\n\n // Insert at specified position\n settings.customModels.splice(actualIndex, 0, newModel);\n\n // Rebuild all IDs\n settings.customModels = rebuildModelIds(settings.customModels);\n\n const insertedModel = settings.customModels[actualIndex];\n\n saveSettings(settings);\n\n return {\n success: true,\n model: insertedModel,\n message: `Added model \"${displayName}\" at index ${actualIndex} with ID \"${insertedModel.id}\"`,\n };\n}\n\n/**\n * Remove a custom model by index, ID, or displayName\n */\nexport function removeModel(identifier: string): {\n success: boolean;\n removed?: CustomModel;\n message: string;\n updatedModels?: { oldId: string; newId: string; displayName: string }[];\n} {\n const settings = loadSettings();\n\n if (!settings.customModels || settings.customModels.length === 0) {\n return {\n success: false,\n message: \"No custom models configured\",\n };\n }\n\n let index: number;\n\n // Try to parse as index number first\n const numericIndex = parseInt(identifier, 10);\n if (!isNaN(numericIndex) && numericIndex >= 0 && numericIndex < settings.customModels.length) {\n index = numericIndex;\n } else {\n // Find by ID or displayName\n index = settings.customModels.findIndex(\n (m) => m.id === identifier || m.displayName === identifier,\n );\n }\n\n if (index === -1) {\n return {\n success: false,\n message: `Model \"${identifier}\" not found. Use index (0-${settings.customModels.length - 1}), ID, or display name.`,\n };\n }\n\n // Store old IDs for tracking updates\n const oldIds = settings.customModels.map((m) => ({ id: m.id, displayName: m.displayName }));\n const removed = settings.customModels.splice(index, 1)[0];\n const oldDefaultId = settings.sessionDefaultSettings?.model;\n\n // Rebuild all IDs\n settings.customModels = rebuildModelIds(settings.customModels);\n\n // Track which models had their IDs updated\n const updatedModels: { oldId: string; newId: string; displayName: string }[] = [];\n for (let i = index; i < settings.customModels.length; i++) {\n const oldInfo = oldIds[i + 1]; // +1 because we removed one\n const newModel = settings.customModels[i];\n if (oldInfo && oldInfo.id !== newModel.id) {\n updatedModels.push({\n oldId: oldInfo.id,\n newId: newModel.id,\n displayName: newModel.displayName,\n });\n }\n }\n\n // Update default model reference if needed\n if (oldDefaultId) {\n if (oldDefaultId === removed.id) {\n // Default was the removed model, clear it\n delete settings.sessionDefaultSettings!.model;\n } else {\n // Check if default model's ID was updated\n const updatedDefault = updatedModels.find((u) => u.oldId === oldDefaultId);\n if (updatedDefault) {\n settings.sessionDefaultSettings!.model = updatedDefault.newId;\n }\n }\n }\n\n saveSettings(settings);\n\n return {\n success: true,\n removed,\n message: `Removed model \"${removed.displayName}\" (was at index ${index})`,\n updatedModels,\n };\n}\n\n/**\n * List all custom models\n */\nexport function listModels(): CustomModel[] {\n const settings = loadSettings();\n return settings.customModels || [];\n}\n\n/**\n * Get current default model\n */\nexport function getDefaultModel(): string | undefined {\n const settings = loadSettings();\n return settings.sessionDefaultSettings?.model;\n}\n\n/**\n * Print models list with detailed info\n */\nexport function printModelsList(): void {\n const models = listModels();\n const defaultModel = getDefaultModel();\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Custom Models\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n if (models.length === 0) {\n console.log(styleText(\"gray\", \" No custom models configured.\"));\n console.log();\n console.log(styleText(\"gray\", \" Add one with: npx droid-patch add-model\"));\n } else {\n console.log(styleText(\"white\", ` Found ${models.length} model(s):`));\n console.log();\n\n for (let i = 0; i < models.length; i++) {\n const model = models[i];\n const isDefault = model.id === defaultModel;\n const defaultMark = isDefault ? styleText(\"green\", \" [DEFAULT]\") : \"\";\n const indexMark = styleText(\"gray\", `[${i}]`);\n\n console.log(` ${indexMark} ${styleText([\"cyan\", \"bold\"], model.displayName)}${defaultMark}`);\n console.log(styleText(\"gray\", ` ID: ${model.id}`));\n console.log(styleText(\"gray\", ` Model: ${model.model}`));\n console.log(styleText(\"gray\", ` Provider: ${model.provider}`));\n console.log(styleText(\"gray\", ` Base URL: ${model.baseUrl}`));\n console.log(styleText(\"gray\", ` API Key: ${model.apiKey.substring(0, 8)}...`));\n console.log();\n }\n }\n\n console.log(styleText(\"gray\", ` Settings file: ${SETTINGS_PATH}`));\n console.log();\n}\n\n/**\n * Interactive prompt helper using readline/promises\n */\nasync function prompt(question: string, defaultValue?: string): Promise<string> {\n const defaultHint = defaultValue ? ` (${defaultValue})` : \"\";\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const answer = await rl.question(`${question}${defaultHint}: `);\n return answer.trim() || defaultValue || \"\";\n } finally {\n rl.close();\n }\n}\n\n/**\n * Interactive prompt for selecting from options\n */\nasync function promptSelect(question: string, options: string[]): Promise<string> {\n console.log(question);\n options.forEach((opt, i) => {\n console.log(styleText(\"cyan\", ` ${i + 1}. ${opt}`));\n });\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const answer = await rl.question(\"Select (number): \");\n const idx = parseInt(answer.trim(), 10) - 1;\n if (idx >= 0 && idx < options.length) {\n return options[idx];\n }\n return options[0];\n } finally {\n rl.close();\n }\n}\n\n/**\n * Interactive mode for adding a model\n */\nexport async function addModelInteractive(insertIndex?: number): Promise<void> {\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Add Custom Model (Interactive)\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n const models = listModels();\n if (models.length > 0) {\n console.log(styleText(\"gray\", `Current models: ${models.length}`));\n models.forEach((m, i) => {\n console.log(styleText(\"gray\", ` [${i}] ${m.displayName}`));\n });\n console.log();\n }\n\n const displayName = await prompt(\"Display name (e.g., 'Opus [proxy]')\");\n if (!displayName) {\n console.log(styleText(\"red\", \"Display name is required\"));\n return;\n }\n\n const modelName = await prompt(\"Model name (e.g., 'claude-sonnet-4-20250514')\");\n if (!modelName) {\n console.log(styleText(\"red\", \"Model name is required\"));\n return;\n }\n\n const baseUrl = await prompt(\"Base URL (e.g., 'http://127.0.0.1:20002/droid')\");\n if (!baseUrl) {\n console.log(styleText(\"red\", \"Base URL is required\"));\n return;\n }\n\n const apiKey = await prompt(\"API Key\");\n if (!apiKey) {\n console.log(styleText(\"red\", \"API Key is required\"));\n return;\n }\n\n const provider = (await promptSelect(\"Provider:\", [\n \"anthropic\",\n \"openai\",\n \"generic-chat-completion-api\",\n ])) as Provider;\n\n let actualIndex = insertIndex;\n if (actualIndex === undefined && models.length > 0) {\n const indexStr = await prompt(`Insert at index (0-${models.length})`, String(models.length));\n actualIndex = parseInt(indexStr, 10);\n if (isNaN(actualIndex) || actualIndex < 0 || actualIndex > models.length) {\n actualIndex = models.length;\n }\n }\n\n console.log();\n const result = addModel(modelName, displayName, baseUrl, apiKey, provider, actualIndex);\n\n if (result.success) {\n console.log(styleText(\"green\", `[+] ${result.message}`));\n console.log();\n console.log(styleText(\"white\", \"Model details:\"));\n console.log(styleText(\"gray\", ` ID: ${result.model.id}`));\n console.log(styleText(\"gray\", ` Display Name: ${result.model.displayName}`));\n console.log(styleText(\"gray\", ` Model: ${result.model.model}`));\n console.log(styleText(\"gray\", ` Provider: ${result.model.provider}`));\n console.log(styleText(\"gray\", ` Base URL: ${result.model.baseUrl}`));\n } else {\n console.log(styleText(\"red\", `Error: ${result.message}`));\n }\n}\n","import bin from \"tiny-bin\";\nimport { styleText } from \"node:util\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\nimport { patchDroid, type Patch } from \"./patcher.ts\";\nimport {\n createAlias,\n removeAlias,\n listAliases,\n createAliasForWrapper,\n clearAllAliases,\n removeAliasesByFilter,\n type FilterFlag,\n} from \"./alias.ts\";\nimport { createWebSearchUnifiedFiles } from \"./websearch-patch.ts\";\nimport {\n saveAliasMetadata,\n createMetadata,\n loadAliasMetadata,\n listAllMetadata,\n formatPatches,\n} from \"./metadata.ts\";\nimport {\n addModel,\n addModelInteractive,\n removeModel,\n printModelsList,\n type Provider,\n} from \"./model-manager.ts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst IS_WINDOWS = platform() === \"win32\";\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst version = getVersion();\n\nfunction getDroidVersion(droidPath: string): string | undefined {\n try {\n const result = execSync(`\"${droidPath}\" --version`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 5000,\n }).trim();\n // Parse version from output like \"droid 1.2.3\" or just \"1.2.3\"\n const match = result.match(/(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : result || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction findDefaultDroidPath(): string {\n const home = homedir();\n\n // Windows: use `where` command instead of `which`\n if (IS_WINDOWS) {\n try {\n const result = execSync(\"where droid\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n // `where` may return multiple lines, take the first one\n const firstResult = result.split(/\\r?\\n/)[0];\n if (firstResult && existsSync(firstResult)) {\n return firstResult;\n }\n } catch {\n // where command failed, continue with fallback paths\n }\n\n // Windows common installation paths\n const windowsPaths = [\n // Default install location\n join(home, \".droid\", \"bin\", \"droid.exe\"),\n // AppData local\n join(home, \"AppData\", \"Local\", \"Programs\", \"droid\", \"droid.exe\"),\n // Scoop\n join(home, \"scoop\", \"apps\", \"droid\", \"current\", \"droid.exe\"),\n // Current directory\n \"./droid.exe\",\n ];\n\n for (const p of windowsPaths) {\n if (existsSync(p)) return p;\n }\n\n // Return default path even if not found (will error later with helpful message)\n return join(home, \".droid\", \"bin\", \"droid.exe\");\n }\n\n // Unix: Try `which droid` first to find droid in PATH\n try {\n const result = execSync(\"which droid\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n if (result && existsSync(result)) {\n return result;\n }\n } catch {\n // which command failed, continue with fallback paths\n }\n\n // Common installation paths (Unix)\n const paths = [\n // Default sh install location\n join(home, \".droid\", \"bin\", \"droid\"),\n // Homebrew on Apple Silicon\n \"/opt/homebrew/bin/droid\",\n // Homebrew on Intel Mac / Linux\n \"/usr/local/bin/droid\",\n // Linux system-wide\n \"/usr/bin/droid\",\n // Current directory\n \"./droid\",\n ];\n\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n\n // Return default path even if not found (will error later with helpful message)\n return join(home, \".droid\", \"bin\", \"droid\");\n}\n\nbin(\"droid-patch\", \"CLI tool to patch droid binary with various modifications\")\n .package(\"droid-patch\", version)\n .option(\n \"--is-custom\",\n \"Patch isCustom:!0 to isCustom:!1 (enable context compression for custom models)\",\n )\n .option(\n \"--skip-login\",\n \"Inject a fake FACTORY_API_KEY to bypass login requirement (no real key needed)\",\n )\n .option(\n \"--api-base <url>\",\n \"Replace API URL (standalone: binary patch, max 22 chars; with --websearch: proxy forward target, no limit)\",\n )\n .option(\n \"--websearch\",\n \"Enable local WebSearch proxy with external providers (Smithery, Google PSE, etc.)\",\n )\n .option(\n \"--websearch-proxy\",\n \"Enable native provider websearch (requires proxy plugin, reads ~/.factory/settings.json)\",\n )\n .option(\"--standalone\", \"Standalone mode: mock non-LLM Factory APIs (use with --websearch)\")\n .option(\n \"--reasoning-effort\",\n \"Enable reasoning effort for custom models (set to high, enable UI selector)\",\n )\n .option(\n \"--disable-telemetry\",\n \"Disable telemetry and Sentry error reporting (block data uploads)\",\n )\n .option(\"--dry-run\", \"Verify patches without actually modifying the binary\")\n .option(\"-p, --path <path>\", \"Path to the droid binary\")\n .option(\"-o, --output <dir>\", \"Output directory for patched binary\")\n .option(\"--no-backup\", \"Do not create backup of original binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .argument(\"[alias]\", \"Alias name for the patched binary\")\n .action(async (options, args) => {\n const alias = args?.[0] as string | undefined;\n const isCustom = options[\"is-custom\"] as boolean;\n const skipLogin = options[\"skip-login\"] as boolean;\n const apiBase = options[\"api-base\"] as string | undefined;\n const websearch = options[\"websearch\"] as boolean;\n const websearchProxy = options[\"websearch-proxy\"] as boolean;\n const standalone = options[\"standalone\"] as boolean;\n // When --websearch is used with --api-base, forward to custom URL\n // Otherwise forward to official Factory API\n const websearchTarget = websearch ? apiBase || \"https://api.factory.ai\" : undefined;\n const reasoningEffort = options[\"reasoning-effort\"] as boolean;\n const noTelemetry = options[\"disable-telemetry\"] as boolean;\n const dryRun = options[\"dry-run\"] as boolean;\n const path = (options.path as string) || findDefaultDroidPath();\n const outputDir = options.output as string | undefined;\n const backup = options.backup !== false;\n const verbose = options.verbose as boolean;\n\n // If -o is specified with alias, output to that directory with alias name\n const outputPath = outputDir && alias ? join(outputDir, alias) : undefined;\n\n const needsBinaryPatch =\n !!isCustom ||\n !!skipLogin ||\n !!reasoningEffort ||\n !!noTelemetry ||\n (!!apiBase && !websearch && !websearchProxy);\n\n // Check for conflicting flags\n if (websearch && websearchProxy) {\n console.log(styleText(\"red\", \"Error: Cannot use --websearch and --websearch-proxy together\"));\n console.log(styleText(\"gray\", \"Choose one:\"));\n console.log(\n styleText(\"gray\", \" --websearch External providers (Smithery, Google PSE, etc.)\"),\n );\n console.log(\n styleText(\"gray\", \" --websearch-proxy Native provider (requires proxy plugin)\"),\n );\n process.exit(1);\n }\n\n // Wrapper-only mode (no binary patching needed):\n // - --websearch or --websearch-proxy (optional --standalone)\n const isWebsearchMode = websearch || websearchProxy;\n if (!needsBinaryPatch && isWebsearchMode) {\n if (!alias) {\n const flag = websearchProxy ? \"--websearch-proxy\" : \"--websearch\";\n console.log(styleText(\"red\", `Error: Alias name required for ${flag}`));\n console.log(styleText(\"gray\", `Usage: npx droid-patch ${flag} <alias>`));\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Wrapper Setup\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"white\", `WebSearch: native provider mode`));\n console.log(styleText(\"gray\", ` Requires proxy plugin (anthropic4droid)`));\n console.log(styleText(\"gray\", ` Reads model config from ~/.factory/settings.json`));\n } else if (websearch) {\n console.log(styleText(\"white\", `WebSearch: external providers mode`));\n console.log(styleText(\"white\", `Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", `Standalone mode: enabled`));\n }\n console.log();\n\n let execTargetPath = path;\n // Create websearch proxy files (proxy script + wrapper)\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n // For --websearch-proxy, apiBase comes from settings.json at runtime\n // For --websearch, use apiBase or default Factory API\n const forwardTarget = websearchProxy ? undefined : websearchTarget;\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n forwardTarget,\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n // Create alias pointing to outer wrapper\n const aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: false,\n skipLogin: false,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: false,\n noTelemetry: false,\n standalone: standalone,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" Wrapper Ready!\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(\"Run directly:\");\n console.log(styleText(\"yellow\", ` ${alias}`));\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"Native Provider WebSearch (--websearch-proxy):\"));\n console.log(styleText(\"gray\", \" Uses model's built-in websearch via proxy plugin\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n console.log();\n console.log(styleText(\"yellow\", \"IMPORTANT: Requires proxy plugin (anthropic4droid)\"));\n console.log();\n console.log(\"Supported providers:\");\n console.log(styleText(\"yellow\", \" - anthropic: Claude web_search_20250305 server tool\"));\n console.log(styleText(\"yellow\", \" - openai: OpenAI web_search tool\"));\n console.log(styleText(\"gray\", \" - generic-chat-completion-api: Not supported\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1 # Basic logs\"));\n console.log(styleText(\"gray\", \" export DROID_SEARCH_VERBOSE=1 # Full request/response\"));\n } else if (websearch) {\n console.log(styleText(\"cyan\", \"External Providers WebSearch (--websearch):\"));\n console.log(\n styleText(\"gray\", \" Uses external search providers (Smithery, Google PSE, etc.)\"),\n );\n console.log();\n console.log(\"Search providers (in priority order):\");\n console.log(styleText(\"yellow\", \" 1. Smithery Exa (best quality):\"));\n console.log(styleText(\"gray\", \" export SMITHERY_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export SMITHERY_PROFILE=your_profile\"));\n console.log(styleText(\"gray\", \" 2. Google PSE:\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" export GOOGLE_PSE_CX=your_search_engine_id\"));\n console.log(styleText(\"gray\", \" 3. Tavily:\"));\n console.log(styleText(\"gray\", \" export TAVILY_API_KEY=your_api_key\"));\n console.log(styleText(\"gray\", \" 4-7. Serper, Brave, SearXNG, DuckDuckGo (fallbacks)\"));\n console.log();\n console.log(\"Debug mode:\");\n console.log(styleText(\"gray\", \" export DROID_SEARCH_DEBUG=1\"));\n }\n return;\n }\n\n if (!isCustom && !skipLogin && !apiBase && !websearch && !reasoningEffort && !noTelemetry) {\n console.log(styleText(\"yellow\", \"No patch flags specified. Available patches:\"));\n console.log(styleText(\"gray\", \" --is-custom Patch isCustom for custom models\"));\n console.log(\n styleText(\"gray\", \" --skip-login Bypass login by injecting a fake API key\"),\n );\n console.log(\n styleText(\n \"gray\",\n \" --api-base Replace API URL (standalone: max 22 chars; with --websearch: no limit)\",\n ),\n );\n console.log(styleText(\"gray\", \" --websearch Enable local WebSearch proxy\"));\n console.log(\n styleText(\"gray\", \" --reasoning-effort Set reasoning effort level for custom models\"),\n );\n console.log(\n styleText(\"gray\", \" --disable-telemetry Disable telemetry and Sentry error reporting\"),\n );\n console.log(\n styleText(\"gray\", \" --standalone Standalone mode: mock non-LLM Factory APIs\"),\n );\n console.log();\n console.log(\"Usage examples:\");\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom droid-custom\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --skip-login droid-nologin\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --is-custom --skip-login droid-patched\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch droid-search\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --websearch --standalone droid-local\"));\n console.log(styleText(\"cyan\", \" npx droid-patch --disable-telemetry droid-private\"));\n console.log(\n styleText(\n \"cyan\",\n \" npx droid-patch --websearch --api-base=http://127.0.0.1:20002 my-droid\",\n ),\n );\n process.exit(1);\n }\n\n if (!alias && !dryRun) {\n console.log(styleText(\"red\", \"Error: alias name is required\"));\n console.log(\n styleText(\n \"gray\",\n \"Usage: droid-patch [--is-custom] [--skip-login] [-o <dir>] <alias-name>\",\n ),\n );\n process.exit(1);\n }\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid Binary Patcher\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n const patches: Patch[] = [];\n if (isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n // Add skip-login patch: replace process.env.FACTORY_API_KEY with a fixed fake key\n // \"process.env.FACTORY_API_KEY\" is 27 chars, we replace with \"fk-droid-patch-skip-00000\" (25 chars + quotes = 27)\n if (skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: 'Replace process.env.FACTORY_API_KEY with \"fk-droid-patch-skip-00000\"',\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Add api-base patch: replace the Factory API base URL\n // Original: \"https://api.factory.ai\" (22 chars)\n // We need to pad the replacement URL to be exactly 22 chars\n // Note: When --websearch is used, --api-base sets the forward target instead of binary patching\n if (apiBase && !websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const originalLength = originalUrl.length; // 22 chars\n\n // Validate and normalize the URL\n let normalizedUrl = apiBase.replace(/\\/+$/, \"\"); // Remove trailing slashes\n\n if (normalizedUrl.length > originalLength) {\n console.log(\n styleText(\"red\", `Error: API base URL must be ${originalLength} characters or less`),\n );\n console.log(\n styleText(\"gray\", ` Your URL: \"${normalizedUrl}\" (${normalizedUrl.length} chars)`),\n );\n console.log(styleText(\"gray\", ` Maximum: ${originalLength} characters`));\n console.log();\n console.log(styleText(\"yellow\", \"Tip: Use a shorter URL or set up a local redirect.\"));\n console.log(styleText(\"gray\", \" Examples:\"));\n console.log(styleText(\"gray\", \" http://127.0.0.1:3000 (19 chars)\"));\n console.log(styleText(\"gray\", \" http://localhost:80 (19 chars)\"));\n process.exit(1);\n }\n\n // Pad the URL with spaces at the end to match original length\n // Note: trailing spaces in URL are generally ignored\n const paddedUrl = normalizedUrl.padEnd(originalLength, \" \");\n\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${normalizedUrl}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n // Add reasoning-effort patch: set custom models to use \"high\" reasoning\n // Also modify UI conditions to show reasoning selector for custom models\n if (reasoningEffort) {\n // Expand custom-model supported reasoning efforts to include \"xhigh\" in the UI.\n //\n // We can't freely change embedded JS string lengths without corrupting the bundle, so rewrites must\n // preserve byte length. We avoid whitespace padding by expanding supportedReasoningEfforts to 3 values.\n //\n // Targets:\n // - Custom model list builder (maps C => { ... supportedReasoningEfforts:[\"none\"] ... })\n // - Custom model resolver (t9 / getTuiModelConfig)\n patches.push({\n name: \"reasoningEffortSupportedXHighList\",\n description: 'Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model list',\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n regexPattern:\n /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.(?:displayName|id),modelProvider:\\1\\.provider\\s*,supportedReasoningEfforts:\\[\"(?:none|high)\"(?:,\"xhigh\")?\\],defaultReasoningEffort:\"(?:none|high)\",isCustom:!([01]),noImageSupport:(?:\\1\\.noImageSupport|!1)/g,\n regexReplacement:\n 'id:$1.id,displayName:$1.displayName,shortDisplayName:$1.displayName,modelProvider:$1.provider,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$2,noImageSupport:!1',\n alreadyPatchedRegexPattern:\n /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.displayName,modelProvider:\\1\\.provider,supportedReasoningEfforts:\\[\"low\",\"high\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n });\n\n patches.push({\n name: \"reasoningEffortSupportedXHighResolver\",\n description: 'Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model config resolver',\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n regexPattern:\n /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider\\s*,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:\\[\"(?:none|high)\"(?:,\"xhigh\")?\\],defaultReasoningEffort:\"(?:none|high)\",isCustom:!([01]),noImageSupport:(?:\\1\\.noImageSupport|!1)/g,\n regexReplacement:\n 'id:$1.model,modelProvider:$1.provider,displayName:$2,shortDisplayName:$2,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$3,noImageSupport:!1',\n alreadyPatchedRegexPattern:\n /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:\\[\"low\",\"high\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n });\n\n // [\"none\"] is 8 chars, [\"high\"] is 8 chars - perfect match!\n patches.push({\n name: \"reasoningEffortSupported\",\n description:\n 'Fallback: Change supportedReasoningEfforts:[\"none\"] to [\"high\"] (for legacy/custom: configs)',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n\n // \"none\" is 4 chars, \"high\" is 4 chars - perfect match!\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Fallback: Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n\n // Change UI condition from length>1 to length>0\n // This allows custom models with single reasoning option to show the selector\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n\n // Change UI condition from length<=1 to length<=0\n // This enables the reasoning setting in /settings menu for custom models\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n\n // Bypass reasoning effort validation to allow settings.json override\n // This allows \"xhigh\" in settings.json to work even though default is \"high\"\n // Pattern varies by version:\n // v0.39.0+: T!==\"none\"&&T!==\"off\"&&!X.supportedReasoningEfforts.includes(T)\n // v0.43.0+: T!==\"none\"&&T!==\"off\"&&!R.reasoningEffort.supported.includes(T)\n // v0.49.0+: !this.validateReasoningEffort(D,H.reasoningEffort)\n // Using regex to match any single-letter minified variable and preserve property path\n // Logic: && 0 && makes entire condition always false, bypassing validation\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n // Regex captures:\n // $1 = reasoning effort variable (single letter)\n // $2 = model/config variable (single letter)\n // $3 = property path (supportedReasoningEfforts or reasoningEffort.supported)\n regexPattern:\n /([A-Za-z$_])!==\"none\"&&\\1!==\"off\"&&!([A-Za-z$_])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n regexReplacement: '$1!=\"none\"&&$1!=\"off\"&&0&&$2.$3.includes($1)',\n alreadyPatchedRegexPattern:\n /([A-Za-z$_])!=\"none\"&&\\1!=\"off\"&&0&&([A-Za-z$_])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n });\n }\n\n // Add no-telemetry patches: disable telemetry uploads and Sentry error reporting\n // Strategy:\n // 1. Break environment variable names so Sentry is never initialized (Q1() returns false)\n // 2. Invert flushToWeb condition so it returns early without making any fetch request\n if (noTelemetry) {\n // Patch 1: Break Sentry environment variable checks\n // Q1() function checks: VITE_VERCEL_ENV, ENABLE_SENTRY, NEXT_PUBLIC_ENABLE_SENTRY, FACTORY_ENABLE_SENTRY\n // By changing first letter to X, the env vars will never match, so Q1() returns false\n // and Sentry is never initialized\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n\n // Patch 2: Make flushToWeb always return early to prevent ANY fetch request\n // Original: if(this.webEvents.length===0)return; // returns only when empty\n // Changed: if(!0||this.webEvents.length)return; // !0=true, ALWAYS returns\n // Result: Function always exits immediately, no telemetry is ever sent\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n try {\n const result = await patchDroid({\n inputPath: path,\n outputPath: outputPath,\n patches,\n dryRun,\n backup,\n verbose,\n });\n\n if (dryRun) {\n console.log();\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"blue\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"gray\", \"To apply the patches, run without --dry-run:\"));\n console.log(styleText(\"cyan\", ` npx droid-patch --is-custom ${alias || \"<alias-name>\"}`));\n process.exit(0);\n }\n\n // If -o is specified, just output the file without creating alias\n if (outputDir && result.success && result.outputPath) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log();\n console.log(styleText(\"white\", `Patched binary saved to: ${result.outputPath}`));\n process.exit(0);\n }\n\n if (result.success && result.outputPath && alias) {\n console.log();\n\n let execTargetPath = result.outputPath;\n\n if (websearch || websearchProxy) {\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n alias,\n websearchProxy ? undefined : websearchTarget, // websearchProxy reads from settings.json at runtime\n standalone,\n websearchProxy, // useNativeProvider flag\n );\n execTargetPath = wrapperScript;\n\n console.log();\n if (websearchProxy) {\n console.log(styleText(\"cyan\", \"WebSearch enabled (native provider mode)\"));\n console.log(styleText(\"gray\", \" Requires proxy plugin (anthropic4droid)\"));\n console.log(styleText(\"gray\", \" Reads model config from ~/.factory/settings.json\"));\n } else {\n console.log(styleText(\"cyan\", \"WebSearch enabled (external providers mode)\"));\n console.log(styleText(\"white\", ` Forward target: ${websearchTarget}`));\n }\n if (standalone) {\n console.log(styleText(\"white\", ` Standalone mode: enabled`));\n }\n }\n\n let aliasResult;\n if (websearch || websearchProxy) {\n aliasResult = await createAliasForWrapper(execTargetPath, alias, verbose);\n } else {\n aliasResult = await createAlias(result.outputPath, alias, verbose);\n }\n\n // Save metadata for update command\n const droidVersion = getDroidVersion(path);\n const metadata = createMetadata(\n alias,\n path,\n {\n isCustom: !!isCustom,\n skipLogin: !!skipLogin,\n apiBase: apiBase || null,\n websearch: !!websearch,\n websearchProxy: !!websearchProxy,\n reasoningEffort: !!reasoningEffort,\n noTelemetry: !!noTelemetry,\n standalone: !!standalone,\n },\n {\n droidPatchVersion: version,\n droidVersion,\n aliasPath: aliasResult.aliasPath,\n },\n );\n await saveAliasMetadata(metadata);\n }\n\n if (result.success) {\n console.log();\n console.log(styleText(\"green\", \"═\".repeat(60)));\n console.log(styleText([\"green\", \"bold\"], \" PATCH SUCCESSFUL\"));\n console.log(styleText(\"green\", \"═\".repeat(60)));\n }\n\n process.exit(result.success ? 0 : 1);\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n if (verbose) console.error((error as Error).stack);\n process.exit(1);\n }\n })\n .command(\"list\", \"List all droid-patch aliases\")\n .action(async () => {\n await listAliases();\n })\n .command(\"remove\", \"Remove alias(es) by name or filter\")\n .argument(\"[alias-or-path]\", \"Alias name or file path to remove\")\n .option(\"--patch-version <version>\", \"Remove aliases created by this droid-patch version\")\n .option(\"--droid-version <version>\", \"Remove aliases for this droid version\")\n .option(\n \"--flag <flag>\",\n \"Remove aliases with this flag (is-custom, skip-login, websearch, api-base, reasoning-effort, disable-telemetry, standalone)\",\n )\n .action(async (options, args) => {\n const target = args?.[0] as string | undefined;\n const patchVersion = options[\"patch-version\"] as string | undefined;\n const droidVersion = options[\"droid-version\"] as string | undefined;\n const flagRaw = options.flag as string | undefined;\n let flag: FilterFlag | undefined;\n if (flagRaw) {\n const allowedFlags: FilterFlag[] = [\n \"is-custom\",\n \"skip-login\",\n \"websearch\",\n \"api-base\",\n \"reasoning-effort\",\n \"disable-telemetry\",\n \"standalone\",\n ];\n if (!allowedFlags.includes(flagRaw as FilterFlag)) {\n console.error(styleText(\"red\", `Error: Invalid --flag value: ${flagRaw}`));\n console.error(styleText(\"gray\", `Allowed: ${allowedFlags.join(\", \")}`));\n process.exit(1);\n }\n flag = flagRaw as FilterFlag;\n }\n\n // If filter options are provided, use filter mode\n if (patchVersion || droidVersion || flag) {\n await removeAliasesByFilter({\n patchVersion,\n droidVersion,\n flags: flag ? [flag] : undefined,\n });\n return;\n }\n\n // If no target and no filter, show error\n if (!target) {\n console.error(\n styleText(\n \"red\",\n \"Error: Provide an alias name or use filter options (--patch-version, --droid-version, --flag)\",\n ),\n );\n process.exit(1);\n }\n\n // Check if it's a file path (contains / or .)\n if (target.includes(\"/\") || existsSync(target)) {\n // It's a file path, delete directly\n const { unlink } = await import(\"node:fs/promises\");\n try {\n await unlink(target);\n console.log(styleText(\"green\", `[*] Removed: ${target}`));\n } catch (error) {\n console.error(styleText(\"red\", `Error: ${(error as Error).message}`));\n process.exit(1);\n }\n } else {\n // It's an alias name\n await removeAlias(target);\n }\n })\n .command(\"version\", \"Print droid-patch version\")\n .action(() => {\n console.log(`droid-patch v${version}`);\n })\n .command(\"clear\", \"Remove all droid-patch aliases and related files\")\n .action(async () => {\n await clearAllAliases();\n })\n .command(\"update\", \"Update aliases with latest droid binary\")\n .argument(\"[alias]\", \"Specific alias to update (optional, updates all if not specified)\")\n .option(\"--dry-run\", \"Preview without making changes\")\n .option(\"-p, --path <path>\", \"Path to new droid binary\")\n .option(\"-v, --verbose\", \"Enable verbose output\")\n .action(async (options, args) => {\n const aliasName = args?.[0] as string | undefined;\n const dryRun = options[\"dry-run\"] as boolean;\n const newBinaryPath = (options.path as string) || findDefaultDroidPath();\n const verbose = options.verbose as boolean;\n\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log(styleText([\"cyan\", \"bold\"], \" Droid-Patch Update\"));\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n console.log();\n\n // Verify the new binary exists\n if (!existsSync(newBinaryPath)) {\n console.log(styleText(\"red\", `Error: Droid binary not found at ${newBinaryPath}`));\n console.log(styleText(\"gray\", \"Use -p to specify a different path\"));\n process.exit(1);\n }\n\n // Get aliases to update\n let metaList: Awaited<ReturnType<typeof loadAliasMetadata>>[];\n if (aliasName) {\n const meta = await loadAliasMetadata(aliasName);\n if (!meta) {\n console.log(styleText(\"red\", `Error: No metadata found for alias \"${aliasName}\"`));\n console.log(\n styleText(\"gray\", \"This alias may have been created before update tracking was added.\"),\n );\n console.log(styleText(\"gray\", \"Remove and recreate the alias to enable update support.\"));\n process.exit(1);\n }\n metaList = [meta];\n } else {\n metaList = await listAllMetadata();\n if (metaList.length === 0) {\n console.log(styleText(\"yellow\", \"No aliases with metadata found.\"));\n console.log(styleText(\"gray\", \"Create aliases with droid-patch to enable update support.\"));\n process.exit(0);\n }\n }\n\n console.log(styleText(\"white\", `Using droid binary: ${newBinaryPath}`));\n console.log(styleText(\"white\", `Found ${metaList.length} alias(es) to update`));\n if (dryRun) {\n console.log(styleText(\"blue\", \"(DRY RUN - no changes will be made)\"));\n }\n console.log();\n\n let successCount = 0;\n let failCount = 0;\n\n for (const meta of metaList) {\n if (!meta) continue;\n\n console.log(styleText(\"cyan\", `─`.repeat(40)));\n console.log(styleText(\"white\", `Updating: ${styleText([\"cyan\", \"bold\"], meta.name)}`));\n console.log(styleText(\"gray\", ` Patches: ${formatPatches(meta.patches)}`));\n\n if (dryRun) {\n console.log(styleText(\"blue\", ` [DRY RUN] Would re-apply patches`));\n successCount++;\n continue;\n }\n\n try {\n // Build patch list based on metadata\n const patches: Patch[] = [];\n\n if (meta.patches.isCustom) {\n patches.push({\n name: \"isCustom\",\n description: \"Change isCustom:!0 to isCustom:!1\",\n pattern: Buffer.from(\"isCustom:!0\"),\n replacement: Buffer.from(\"isCustom:!1\"),\n });\n }\n\n if (meta.patches.skipLogin) {\n patches.push({\n name: \"skipLogin\",\n description: \"Replace process.env.FACTORY_API_KEY with fake key\",\n pattern: Buffer.from(\"process.env.FACTORY_API_KEY\"),\n replacement: Buffer.from('\"fk-droid-patch-skip-00000\"'),\n });\n }\n\n // Only apply apiBase binary patch when NOT using websearch\n // When websearch is enabled, apiBase is used as forward target, not binary patch\n if (meta.patches.apiBase && !meta.patches.websearch) {\n const originalUrl = \"https://api.factory.ai\";\n const paddedUrl = meta.patches.apiBase.padEnd(originalUrl.length, \" \");\n patches.push({\n name: \"apiBase\",\n description: `Replace Factory API URL with \"${meta.patches.apiBase}\"`,\n pattern: Buffer.from(originalUrl),\n replacement: Buffer.from(paddedUrl),\n });\n }\n\n if (meta.patches.reasoningEffort) {\n patches.push({\n name: \"reasoningEffortSupportedXHighList\",\n description: 'Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model list',\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n regexPattern:\n /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.(?:displayName|id),modelProvider:\\1\\.provider\\s*,supportedReasoningEfforts:\\[\"(?:none|high)\"(?:,\"xhigh\")?\\],defaultReasoningEffort:\"(?:none|high)\",isCustom:!([01]),noImageSupport:(?:\\1\\.noImageSupport|!1)/g,\n regexReplacement:\n 'id:$1.id,displayName:$1.displayName,shortDisplayName:$1.displayName,modelProvider:$1.provider,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$2,noImageSupport:!1',\n alreadyPatchedRegexPattern:\n /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.displayName,modelProvider:\\1\\.provider,supportedReasoningEfforts:\\[\"low\",\"high\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n });\n patches.push({\n name: \"reasoningEffortSupportedXHighResolver\",\n description: 'Enable [\"low\",\"high\",\"xhigh\"] in UI for custom model config resolver',\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n regexPattern:\n /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider\\s*,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:\\[\"(?:none|high)\"(?:,\"xhigh\")?\\],defaultReasoningEffort:\"(?:none|high)\",isCustom:!([01]),noImageSupport:(?:\\1\\.noImageSupport|!1)/g,\n regexReplacement:\n 'id:$1.model,modelProvider:$1.provider,displayName:$2,shortDisplayName:$2,supportedReasoningEfforts:[\"low\",\"high\",\"xhigh\"],defaultReasoningEffort:\"high\",isCustom:!$3,noImageSupport:!1',\n alreadyPatchedRegexPattern:\n /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:\\[\"low\",\"high\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n });\n patches.push({\n name: \"reasoningEffortSupported\",\n description:\n 'Fallback: Change supportedReasoningEfforts:[\"none\"] to [\"high\"] (for legacy/custom: configs)',\n pattern: Buffer.from('supportedReasoningEfforts:[\"none\"]'),\n replacement: Buffer.from('supportedReasoningEfforts:[\"high\"]'),\n });\n patches.push({\n name: \"reasoningEffortDefault\",\n description: 'Fallback: Change defaultReasoningEffort:\"none\" to \"high\"',\n pattern: Buffer.from('defaultReasoningEffort:\"none\"'),\n replacement: Buffer.from('defaultReasoningEffort:\"high\"'),\n });\n patches.push({\n name: \"reasoningEffortUIShow\",\n description: \"Change supportedReasoningEfforts.length>1 to length>0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length>1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length>0\"),\n });\n patches.push({\n name: \"reasoningEffortUIEnable\",\n description: \"Change supportedReasoningEfforts.length<=1 to length<=0\",\n pattern: Buffer.from(\"supportedReasoningEfforts.length<=1\"),\n replacement: Buffer.from(\"supportedReasoningEfforts.length<=0\"),\n });\n patches.push({\n name: \"reasoningEffortValidationBypass\",\n description: \"Bypass reasoning effort validation (allows xhigh in settings.json)\",\n pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n replacement: Buffer.from(\"\"),\n regexPattern:\n /([A-Za-z$_])!==\"none\"&&\\1!==\"off\"&&!([A-Za-z$_])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n regexReplacement: '$1!=\"none\"&&$1!=\"off\"&&0&&$2.$3.includes($1)',\n alreadyPatchedRegexPattern:\n /([A-Za-z$_])!=\"none\"&&\\1!=\"off\"&&0&&([A-Za-z$_])\\.(supportedReasoningEfforts|reasoningEffort\\.supported)\\.includes\\(\\1\\)/g,\n });\n }\n\n if (meta.patches.noTelemetry) {\n patches.push({\n name: \"noTelemetrySentryEnv1\",\n description: \"Break ENABLE_SENTRY env var check (E->X)\",\n pattern: Buffer.from(\"ENABLE_SENTRY\"),\n replacement: Buffer.from(\"XNABLE_SENTRY\"),\n });\n patches.push({\n name: \"noTelemetrySentryEnv2\",\n description: \"Break VITE_VERCEL_ENV env var check (V->X)\",\n pattern: Buffer.from(\"VITE_VERCEL_ENV\"),\n replacement: Buffer.from(\"XITE_VERCEL_ENV\"),\n });\n patches.push({\n name: \"noTelemetryFlushBlock\",\n description: \"Make flushToWeb always return (!0|| = always true)\",\n pattern: Buffer.from(\"this.webEvents.length===0\"),\n replacement: Buffer.from(\"!0||this.webEvents.length\"),\n });\n }\n\n // Determine output path based on whether this is a websearch alias\n const binsDir = join(homedir(), \".droid-patch\", \"bins\");\n const outputPath = join(binsDir, `${meta.name}-patched`);\n\n // Apply patches (only if there are binary patches to apply)\n if (patches.length > 0) {\n const result = await patchDroid({\n inputPath: newBinaryPath,\n outputPath,\n patches,\n dryRun: false,\n backup: false,\n verbose,\n });\n\n if (!result.success) {\n console.log(styleText(\"red\", ` ✗ Failed to apply patches`));\n failCount++;\n continue;\n }\n\n // Re-sign on macOS\n if (process.platform === \"darwin\") {\n try {\n const { execSync } = await import(\"node:child_process\");\n execSync(`codesign --force --deep --sign - \"${outputPath}\"`, {\n stdio: \"pipe\",\n });\n if (verbose) {\n console.log(styleText(\"gray\", ` Re-signed binary`));\n }\n } catch {\n console.log(styleText(\"yellow\", ` [!] Could not re-sign binary`));\n }\n }\n }\n\n let execTargetPath = patches.length > 0 ? outputPath : newBinaryPath;\n\n // If websearch is enabled, regenerate wrapper files\n // Support both new 'websearch' field and old 'proxy' field for backward compatibility\n const hasWebsearch = meta.patches.websearch || !!meta.patches.proxy;\n if (hasWebsearch) {\n // Determine forward target: apiBase > proxy (legacy) > default\n const forwardTarget =\n meta.patches.apiBase || meta.patches.proxy || \"https://api.factory.ai\";\n const proxyDir = join(homedir(), \".droid-patch\", \"proxy\");\n const { wrapperScript } = await createWebSearchUnifiedFiles(\n proxyDir,\n execTargetPath,\n meta.name,\n forwardTarget,\n meta.patches.standalone || false,\n );\n execTargetPath = wrapperScript;\n if (verbose) {\n console.log(styleText(\"gray\", ` Regenerated websearch wrapper`));\n if (meta.patches.standalone) {\n console.log(styleText(\"gray\", ` Standalone mode: enabled`));\n }\n }\n // Migrate old proxy field to new websearch field\n if (meta.patches.proxy && !meta.patches.websearch) {\n meta.patches.websearch = true;\n meta.patches.apiBase = meta.patches.proxy;\n delete meta.patches.proxy;\n }\n }\n\n // If this alias previously used removed features (statusline/sessions), drop legacy flags\n // so the updated alias points directly to the new target wrapper/binary.\n delete (meta.patches as Record<string, unknown>).statusline;\n delete (meta.patches as Record<string, unknown>).sessions;\n\n // Update symlink - find existing or use stored aliasPath\n const { symlink, unlink, readlink, lstat } = await import(\"node:fs/promises\");\n let aliasPath = meta.aliasPath;\n\n // If aliasPath not stored (old version), try to find existing symlink\n if (!aliasPath) {\n const commonPathDirs = [\n join(homedir(), \".local/bin\"),\n join(homedir(), \"bin\"),\n join(homedir(), \".bin\"),\n \"/opt/homebrew/bin\",\n \"/usr/local/bin\",\n join(homedir(), \".droid-patch\", \"aliases\"),\n ];\n\n for (const dir of commonPathDirs) {\n const possiblePath = join(dir, meta.name);\n if (existsSync(possiblePath)) {\n try {\n const stats = await lstat(possiblePath);\n if (stats.isSymbolicLink()) {\n const target = await readlink(possiblePath);\n if (\n target.includes(\".droid-patch/bins\") ||\n target.includes(\".droid-patch/proxy\") ||\n target.includes(\".droid-patch/statusline\")\n ) {\n aliasPath = possiblePath;\n if (verbose) {\n console.log(styleText(\"gray\", ` Found existing symlink: ${aliasPath}`));\n }\n break;\n }\n }\n } catch {\n // Ignore errors, continue searching\n }\n }\n }\n }\n\n // Update symlink if we have a path\n if (aliasPath) {\n try {\n if (existsSync(aliasPath)) {\n const currentTarget = await readlink(aliasPath);\n if (currentTarget !== execTargetPath) {\n await unlink(aliasPath);\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Updated symlink: ${aliasPath}`));\n }\n }\n } else {\n // Symlink doesn't exist, recreate it\n await symlink(execTargetPath, aliasPath);\n if (verbose) {\n console.log(styleText(\"gray\", ` Recreated symlink: ${aliasPath}`));\n }\n }\n // Store aliasPath in metadata for future updates\n meta.aliasPath = aliasPath;\n } catch (symlinkError) {\n console.log(\n styleText(\n \"yellow\",\n ` [!] Could not update symlink: ${(symlinkError as Error).message}`,\n ),\n );\n }\n }\n\n // Update metadata\n meta.updatedAt = new Date().toISOString();\n meta.originalBinaryPath = newBinaryPath;\n meta.droidVersion = getDroidVersion(newBinaryPath);\n meta.droidPatchVersion = version;\n await saveAliasMetadata(meta);\n\n console.log(styleText(\"green\", ` ✓ Updated successfully`));\n successCount++;\n } catch (error) {\n console.log(styleText(\"red\", ` ✗ Error: ${(error as Error).message}`));\n if (verbose) {\n console.error((error as Error).stack);\n }\n failCount++;\n }\n }\n\n console.log();\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n if (dryRun) {\n console.log(styleText([\"blue\", \"bold\"], \" DRY RUN COMPLETE\"));\n console.log(styleText(\"gray\", ` Would update ${successCount} alias(es)`));\n } else if (failCount === 0) {\n console.log(styleText([\"green\", \"bold\"], \" UPDATE COMPLETE\"));\n console.log(styleText(\"gray\", ` Updated ${successCount} alias(es)`));\n } else {\n console.log(styleText([\"yellow\", \"bold\"], \" UPDATE FINISHED WITH ERRORS\"));\n console.log(styleText(\"gray\", ` Success: ${successCount}, Failed: ${failCount}`));\n }\n console.log(styleText(\"cyan\", \"═\".repeat(60)));\n })\n .command(\"add-model\", \"Add a custom model to settings.json (interactive if no options)\")\n .option(\"-m, --model <model>\", \"Model name (e.g., claude-sonnet-4-20250514)\")\n .option(\"-n, --name <name>\", \"Display name (e.g., 'Opus [proxy]')\")\n .option(\"-u, --url <url>\", \"Base URL (e.g., http://127.0.0.1:20002/droid)\")\n .option(\"-k, --key <key>\", \"API key\")\n .option(\n \"-p, --provider <provider>\",\n \"Provider: anthropic, openai, or generic-chat-completion-api\",\n )\n .option(\"-i, --index <index>\", \"Insert at index (auto-assigned if not specified)\")\n .action(async (options) => {\n const model = options.model as string | undefined;\n const displayName = options.name as string | undefined;\n const baseUrl = options.url as string | undefined;\n const apiKey = options.key as string | undefined;\n const providerStr = options.provider as string | undefined;\n const indexStr = options.index as string | undefined;\n\n // If no options provided, enter interactive mode\n if (!model && !displayName && !baseUrl && !apiKey && !providerStr) {\n const index = indexStr ? parseInt(indexStr, 10) : undefined;\n await addModelInteractive(index);\n return;\n }\n\n // If some but not all options provided, show error with usage\n if (!model || !displayName || !baseUrl || !apiKey || !providerStr) {\n console.log(\n styleText(\n \"yellow\",\n \"Missing required options. Enter interactive mode or provide all options.\",\n ),\n );\n console.log();\n console.log(styleText(\"white\", \"Interactive mode:\"));\n console.log(styleText(\"cyan\", \" npx droid-patch add-model\"));\n console.log();\n console.log(styleText(\"white\", \"Full command mode:\"));\n console.log(styleText(\"cyan\", \" npx droid-patch add-model \\\\\"));\n console.log(styleText(\"cyan\", ' -m \"claude-sonnet-4-20250514\" \\\\'));\n console.log(styleText(\"cyan\", ' -n \"Opus [proxy]\" \\\\'));\n console.log(styleText(\"cyan\", ' -u \"http://127.0.0.1:20002/droid\" \\\\'));\n console.log(styleText(\"cyan\", ' -k \"your-api-key\" \\\\'));\n console.log(styleText(\"cyan\", ' -p \"anthropic\"'));\n console.log();\n console.log(styleText(\"gray\", \"Providers: anthropic, openai, generic-chat-completion-api\"));\n console.log(styleText(\"gray\", \"Optional: -i <index> to insert at specific position\"));\n process.exit(1);\n }\n\n const validProviders: Provider[] = [\"anthropic\", \"openai\", \"generic-chat-completion-api\"];\n if (!validProviders.includes(providerStr as Provider)) {\n console.log(styleText(\"red\", `Error: Invalid provider \"${providerStr}\"`));\n console.log(styleText(\"gray\", `Valid providers: ${validProviders.join(\", \")}`));\n process.exit(1);\n }\n\n const index = indexStr ? parseInt(indexStr, 10) : undefined;\n if (indexStr && (isNaN(index!) || index! < 0)) {\n console.log(styleText(\"red\", \"Error: Index must be a non-negative number\"));\n process.exit(1);\n }\n\n const result = addModel(model, displayName, baseUrl, apiKey, providerStr as Provider, index);\n\n if (result.success) {\n console.log(styleText(\"green\", `[+] ${result.message}`));\n console.log();\n console.log(styleText(\"white\", \"Model details:\"));\n console.log(styleText(\"gray\", ` ID: ${result.model.id}`));\n console.log(styleText(\"gray\", ` Display Name: ${result.model.displayName}`));\n console.log(styleText(\"gray\", ` Model: ${result.model.model}`));\n console.log(styleText(\"gray\", ` Provider: ${result.model.provider}`));\n console.log(styleText(\"gray\", ` Base URL: ${result.model.baseUrl}`));\n } else {\n console.log(styleText(\"red\", `Error: ${result.message}`));\n process.exit(1);\n }\n })\n .command(\"remove-model\", \"Remove a custom model from settings.json\")\n .argument(\"<identifier>\", \"Model index, ID, or display name to remove\")\n .action((options, args) => {\n const identifier = args?.[0] as string;\n\n if (!identifier) {\n console.log(styleText(\"red\", \"Error: Model identifier required\"));\n console.log();\n console.log(styleText(\"white\", \"Usage:\"));\n console.log(styleText(\"cyan\", \" npx droid-patch remove-model <identifier>\"));\n console.log();\n console.log(styleText(\"gray\", \"Identifier can be:\"));\n console.log(styleText(\"gray\", \" - Index number (e.g., 0, 1, 2)\"));\n console.log(styleText(\"gray\", \" - Model ID (e.g., custom:Opus-[proxy]-0)\"));\n console.log(styleText(\"gray\", \" - Display name (e.g., 'Opus [proxy]')\"));\n console.log();\n console.log(styleText(\"gray\", \"Use 'npx droid-patch list-models' to see all models\"));\n process.exit(1);\n }\n\n const result = removeModel(identifier);\n\n if (result.success && result.removed) {\n console.log(styleText(\"green\", `[+] ${result.message}`));\n console.log();\n console.log(styleText(\"white\", \"Removed model details:\"));\n console.log(styleText(\"gray\", ` ID: ${result.removed.id}`));\n console.log(styleText(\"gray\", ` Display Name: ${result.removed.displayName}`));\n console.log(styleText(\"gray\", ` Model: ${result.removed.model}`));\n console.log(styleText(\"gray\", ` Provider: ${result.removed.provider}`));\n\n if (result.updatedModels && result.updatedModels.length > 0) {\n console.log();\n console.log(styleText(\"yellow\", \"Updated model IDs (due to index shift):\"));\n for (const updated of result.updatedModels) {\n console.log(styleText(\"gray\", ` ${updated.displayName}:`));\n console.log(styleText(\"gray\", ` ${updated.oldId} → ${updated.newId}`));\n }\n }\n console.log();\n console.log(styleText(\"gray\", \"Use 'npx droid-patch list-models' to see current state.\"));\n } else {\n console.log(styleText(\"red\", `Error: ${result.message}`));\n process.exit(1);\n }\n })\n .command(\"list-models\", \"List all custom models in settings.json\")\n .action(() => {\n printModelsList();\n })\n .run()\n .catch((err: Error) => {\n console.error(err);\n process.exit(1);\n });\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "droid-patch",
3
- "version": "0.13.3",
3
+ "version": "0.13.4",
4
4
  "description": "CLI tool to patch droid binary with various modifications",
5
5
  "homepage": "https://github.com/kingsword09/droid-patch#readme",
6
6
  "bugs": {