{"version":3,"file":"cli.mjs","names":["IS_WINDOWS"],"sources":["../src/runtime-proxy-factory-compat.ts","../src/websearch-external.ts","../src/websearch-native.ts","../src/websearch-patch.ts","../src/model-manager.ts","../src/cli.ts"],"sourcesContent":["/**\n * Shared Factory runtime-compat shims for wrapper-backed Droid sessions.\n *\n * These snippets are injected into the generated runtime proxy scripts used by\n * `--is-custom`, `--skip-login`, `--websearch`, and `--websearch-proxy`.\n *\n * The goal is to keep mission workers, compaction, and other startup flows from\n * depending on a real Factory organization when the session is running through a\n * patched local runtime.\n */\n\nexport function generateFactoryCompatPrelude(): string {\n  return `\nconst MOCK_USER_ID = 'f';\nconst MOCK_ORG_ID = 'f';\nconst SKIP_LOGIN_PATCHED = process.env.DROID_SKIP_LOGIN === '1';\nconst FACTORY_COMPAT_PATCHED = process.env.DROID_FACTORY_COMPAT === '1';\n\nfunction getBearerToken(headers) {\n  const auth = headers && headers.authorization;\n  if (typeof auth !== 'string') return null;\n  const match = auth.match(/^Bearer\\\\s+(.+)$/i);\n  return match ? match[1] : null;\n}\n\nfunction isPatchedFactoryKey(token) {\n  return typeof token === 'string' && /^fk/i.test(token);\n}\n\nfunction createMockBillingLimits(overagePreference) {\n  return {\n    usesTokenRateLimitsBilling: false,\n    overagePreference: overagePreference || null,\n    extraUsageAllowed: false,\n    extraUsageBalanceCents: 0,\n    limits: {\n      standard: {\n        fiveHour: { usedPercent: 0 },\n        weekly: { usedPercent: 0 },\n        monthly: { usedPercent: 0 },\n      },\n    },\n  };\n}\n\nfunction createMockManagedSettings() {\n  return {\n    success: true,\n    factoryTier: 'team',\n    settings: {},\n  };\n}\n\nfunction createMockFeatureFlags() {\n  return {\n    flags: {},\n    configs: {},\n  };\n}\n\nfunction writeJson(res, statusCode, payload) {\n  res.writeHead(statusCode, { 'Content-Type': 'application/json' });\n  res.end(JSON.stringify(payload));\n}\n`;\n}\n\nexport function generateFactoryCompatState(): string {\n  return `\nlet mockedOveragePreference = null;\n`;\n}\n\nexport function generateFactoryCompatRequestGuard(): string {\n  return `\n  const bearerToken = getBearerToken(req.headers);\n  const isPatchedAuthRequest =\n    FACTORY_COMPAT_PATCHED || SKIP_LOGIN_PATCHED || isPatchedFactoryKey(bearerToken);\n`;\n}\n\nexport function generateFactoryCompatRoutes(): string {\n  return `\n  // Patched local sessions do not necessarily have a real Factory organization.\n  // Newer Droid mission/compaction flows probe these endpoints before launching\n  // worker paths, so we return synthetic responses when runtime compat is active.\n  if (isPatchedAuthRequest) {\n    if (pathname === '/api/cli/whoami') {\n      writeJson(res, 200, { userId: MOCK_USER_ID, orgId: MOCK_ORG_ID });\n      return;\n    }\n\n    if (pathname === '/api/organization/managed-settings' && req.method === 'GET') {\n      writeJson(res, 200, createMockManagedSettings());\n      return;\n    }\n\n    if (pathname === '/api/feature-flags' && req.method === 'GET') {\n      writeJson(res, 200, createMockFeatureFlags());\n      return;\n    }\n\n    if (pathname === '/api/billing/limits' && req.method === 'GET') {\n      writeJson(res, 200, createMockBillingLimits(mockedOveragePreference));\n      return;\n    }\n\n    if (pathname === '/api/organization/subscription/set-overage-preference' && req.method === 'POST') {\n      let body = '';\n      req.on('data', function(c) { body += c; });\n      req.on('end', function() {\n        let requestedPreference = null;\n        if (body) {\n          try {\n            const parsed = JSON.parse(body);\n            if (typeof parsed.overagePreference === 'string') {\n              requestedPreference = parsed.overagePreference;\n            }\n          } catch {}\n        }\n\n        mockedOveragePreference = requestedPreference;\n        writeJson(res, 200, {\n          ok: true,\n          overagePreference: mockedOveragePreference,\n        });\n      });\n      return;\n    }\n  }\n`;\n}\n","import {\n  generateFactoryCompatPrelude,\n  generateFactoryCompatRequestGuard,\n  generateFactoryCompatRoutes,\n  generateFactoryCompatState,\n} from \"./runtime-proxy-factory-compat.ts\";\n\n/**\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';\nconst SEARCH_ROUTE_ALIASES = new Set(['/api/tools/web-search', '/api/tools/exa/search']);\n${generateFactoryCompatPrelude().trim()}\n\nfunction log() { if (DEBUG) console.error.apply(console, ['[websearch]'].concat(Array.from(arguments))); }\n\nfunction isSearchRequest(url, method) {\n  return method === 'POST' && SEARCH_ROUTE_ALIASES.has(url.pathname);\n}\n\n${generateFactoryCompatState().trim()}\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  const pathname = url.pathname;\n${generateFactoryCompatRequestGuard().trimEnd()}\n\n  if (pathname === '/health') {\n    writeJson(res, 200, { status: 'ok', mode: 'external-providers' });\n    return;\n  }\n\n  if (isSearchRequest(url, req.method)) {\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${generateFactoryCompatRoutes().trimEnd()}\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","import {\n  generateFactoryCompatPrelude,\n  generateFactoryCompatRequestGuard,\n  generateFactoryCompatRoutes,\n  generateFactoryCompatState,\n} from \"./runtime-proxy-factory-compat.ts\";\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 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}';\nconst SEARCH_ROUTE_ALIASES = new Set(['/api/tools/web-search', '/api/tools/exa/search']);\nconst SUPPORTED_PROVIDERS = new Set(['anthropic', 'openai']);\n${generateFactoryCompatPrelude().trim()}\n\nfunction log(...args) { if (DEBUG) console.error('[websearch]', ...args); }\n\nfunction isSearchRequest(url, method) {\n  return method === 'POST' && SEARCH_ROUTE_ALIASES.has(url.pathname);\n}\n\nlet cachedSettings = null;\nlet settingsLastModified = 0;\nlet lastObservedProvider = null;\n${generateFactoryCompatState().trim()}\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 listCustomModels(settings) {\n  return Array.isArray(settings && settings.customModels) ? settings.customModels : [];\n}\n\nfunction isSupportedModel(modelConfig, preferredProvider) {\n  return !!(\n    modelConfig &&\n    SUPPORTED_PROVIDERS.has(modelConfig.provider) &&\n    (!preferredProvider || modelConfig.provider === preferredProvider) &&\n    modelConfig.id &&\n    modelConfig.baseUrl &&\n    modelConfig.apiKey &&\n    modelConfig.model\n  );\n}\n\nfunction buildCandidateModelIds(settings) {\n  const candidates = [\n    process.env.DROID_SEARCH_MODEL_ID,\n    settings && settings.sessionDefaultSettings && settings.sessionDefaultSettings.model,\n    settings && settings.missionModelSettings && settings.missionModelSettings.workerModel,\n    settings && settings.missionModelSettings && settings.missionModelSettings.validationWorkerModel,\n    settings && settings.missionModelSettings && settings.missionModelSettings.orchestratorModel,\n  ];\n  const unique = [];\n  for (const candidate of candidates) {\n    if (candidate && !unique.includes(candidate)) unique.push(candidate);\n  }\n  return unique;\n}\n\nfunction summarizeSupportedModels(settings, preferredProvider) {\n  return listCustomModels(settings)\n    .filter(function(modelConfig) { return isSupportedModel(modelConfig, preferredProvider); })\n    .map(function(modelConfig) { return modelConfig.id + ' [' + modelConfig.provider + ']'; })\n    .join(', ');\n}\n\nfunction getCurrentModelConfig(preferredProvider) {\n  const settings = getFactorySettings();\n  if (!settings) {\n    return {\n      error: 'Failed to load ~/.factory/settings.json for native websearch',\n      statusCode: 500,\n    };\n  }\n\n  const customModels = listCustomModels(settings);\n  const candidateIds = buildCandidateModelIds(settings);\n  for (const candidateId of candidateIds) {\n    const modelConfig = customModels.find(function(model) {\n      return model.id === candidateId && isSupportedModel(model, preferredProvider);\n    });\n    if (modelConfig) {\n      lastObservedProvider = modelConfig.provider;\n      log('Resolved model:', modelConfig.id, '| Provider:', modelConfig.provider);\n      return { modelConfig: modelConfig, source: candidateId };\n    }\n  }\n\n  const fallbackModels = customModels.filter(function(modelConfig) {\n    return isSupportedModel(modelConfig, preferredProvider);\n  });\n  if (fallbackModels.length === 1) {\n    lastObservedProvider = fallbackModels[0].provider;\n    log('Falling back to only supported model:', fallbackModels[0].id);\n    return { modelConfig: fallbackModels[0], source: 'single-supported-model' };\n  }\n\n  const providerLabel = preferredProvider || 'anthropic/openai';\n  const supported = summarizeSupportedModels(settings, preferredProvider);\n  const message = supported\n    ? 'Could not resolve an active ' + providerLabel + ' custom model for native websearch. Available models: ' + supported\n    : 'No supported ' + providerLabel + ' custom models found in ~/.factory/settings.json';\n\n  return { error: message, statusCode: 400 };\n}\n\nfunction normalizeEndpoint(baseUrl, suffix) {\n  return baseUrl.endsWith(suffix) ? baseUrl : baseUrl.replace(/\\\\/$/, '') + suffix;\n}\n\nfunction extractErrorMessage(value) {\n  if (!value) return 'Unknown upstream error';\n  if (typeof value === 'string') return value;\n  if (typeof value.message === 'string') return value.message;\n  return JSON.stringify(value);\n}\n\nfunction sleep(ms) {\n  return new Promise(function(resolve) { setTimeout(resolve, ms); });\n}\n\nfunction getRetryDelayMs(attempt) {\n  return Math.min(250 * Math.pow(2, attempt - 1), 2000);\n}\n\nfunction isRetryableSearchFailure(statusCode, message) {\n  if (statusCode === 408 || statusCode === 425 || statusCode === 429) return true;\n  if (statusCode >= 500 && statusCode <= 599) return true;\n\n  const normalized = String(message || '').toLowerCase();\n  return normalized.includes('proxy failed:') ||\n    normalized.includes('client network socket disconnected before secure tls connection was established') ||\n    normalized.includes('fetch failed') ||\n    normalized.includes('socket hang up') ||\n    normalized.includes('request timed out') ||\n    normalized.includes('timed out') ||\n    normalized.includes('econnreset') ||\n    normalized.includes('ecconnreset') ||\n    normalized.includes('econnrefused') ||\n    normalized.includes('ehostunreach') ||\n    normalized.includes('enotfound') ||\n    normalized.includes('eai_again');\n}\n\nfunction pushUniqueResult(results, result) {\n  if (!result || !result.url) return;\n  if (results.some(function(existing) { return existing.url === result.url; })) return;\n  results.push({\n    title: result.title || result.url,\n    url: result.url,\n    content: result.content || '',\n  });\n}\n\nfunction parseOpenAITextResults(text, numResults) {\n  const results = [];\n  const lines = String(text || '').split(/\\\\r?\\\\n/);\n  let current = null;\n\n  function flushCurrent() {\n    if (!current || !current.url) {\n      current = null;\n      return;\n    }\n    pushUniqueResult(results, {\n      title: current.title,\n      url: current.url,\n      content: current.content.join(' ').trim(),\n    });\n    current = null;\n  }\n\n  for (const rawLine of lines) {\n    const line = rawLine.trim();\n    if (!line) continue;\n\n    const titleMatch = line.match(/^\\\\d+\\\\.\\\\s+(?:\\\\*\\\\*(.+?)\\\\*\\\\*|(.+))$/);\n    if (titleMatch) {\n      flushCurrent();\n      current = {\n        title: (titleMatch[1] || titleMatch[2] || '').trim(),\n        url: '',\n        content: [],\n      };\n      continue;\n    }\n\n    const urlMatch = line.match(/^(?:[-*]\\\\s+)?(https?:\\\\/\\\\/\\\\S+)/);\n    if (urlMatch) {\n      if (!current) {\n        current = { title: urlMatch[1], url: '', content: [] };\n      }\n      current.url = urlMatch[1].replace(/[),.;]+$/, '');\n      continue;\n    }\n\n    const bulletTextMatch = line.match(/^[-*]\\\\s+(.+)/);\n    const contentText = (bulletTextMatch ? bulletTextMatch[1] : line).trim();\n    if (!current) {\n      continue;\n    }\n    if (contentText) {\n      current.content.push(contentText);\n    }\n  }\n\n  flushCurrent();\n  return results.slice(0, numResults);\n}\n\nasync function postJson(endpoint, headers, requestBody) {\n  const maxAttempts = 5;\n  let lastError = null;\n\n  for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n    const controller = new AbortController();\n    const timeoutId = setTimeout(function() { controller.abort(); }, 60000);\n\n    try {\n      const response = await fetch(endpoint, {\n        method: 'POST',\n        headers: headers,\n        body: JSON.stringify(requestBody),\n        signal: controller.signal,\n      });\n      const responseText = await response.text();\n      let payload = {};\n      if (responseText) {\n        try {\n          payload = JSON.parse(responseText);\n        } catch {\n          throw new Error('Invalid JSON response from ' + endpoint);\n        }\n      }\n\n      if (!response.ok) {\n        const message = extractErrorMessage(payload && payload.error) || ('HTTP ' + response.status);\n        if (attempt < maxAttempts && isRetryableSearchFailure(response.status, message)) {\n          lastError = new Error(message);\n          const delayMs = getRetryDelayMs(attempt);\n          log('Retrying request after upstream error:', response.status, message, '| next attempt', attempt + 1, 'of', maxAttempts);\n          await sleep(delayMs);\n          continue;\n        }\n        throw new Error(message);\n      }\n\n      if (payload && payload.error) {\n        const message = extractErrorMessage(payload.error);\n        if (attempt < maxAttempts && isRetryableSearchFailure(undefined, message)) {\n          lastError = new Error(message);\n          const delayMs = getRetryDelayMs(attempt);\n          log('Retrying request after payload error:', message, '| next attempt', attempt + 1, 'of', maxAttempts);\n          await sleep(delayMs);\n          continue;\n        }\n        throw new Error(message);\n      }\n\n      return payload;\n    } catch (e) {\n      const message = e && e.name === 'AbortError' ? 'Request timed out' : (e && e.message ? e.message : String(e));\n      if (attempt < maxAttempts && isRetryableSearchFailure(undefined, message)) {\n        lastError = new Error(message);\n        const delayMs = getRetryDelayMs(attempt);\n        log('Retrying request after transport error:', message, '| next attempt', attempt + 1, 'of', maxAttempts);\n        await sleep(delayMs);\n        continue;\n      }\n      throw new Error(message);\n    } finally {\n      clearTimeout(timeoutId);\n    }\n  }\n\n  throw lastError || new Error('Request failed');\n}\n\nasync function searchAnthropicNative(query, numResults, modelConfig) {\n  const endpoint = normalizeEndpoint(modelConfig.baseUrl, '/v1/messages');\n  const requestBody = {\n    model: modelConfig.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  log('Anthropic search:', query, '→', endpoint);\n  const response = await postJson(endpoint, {\n    'Content-Type': 'application/json',\n    'anthropic-version': '2023-06-01',\n    'x-api-key': modelConfig.apiKey,\n  }, requestBody);\n\n  const results = [];\n  for (const block of (response.content || [])) {\n    if (block.type !== 'web_search_tool_result') continue;\n    for (const result of (block.content || [])) {\n      if (result.type !== 'web_search_result') continue;\n      results.push({\n        title: result.title || '',\n        url: result.url || '',\n        content: result.snippet || result.page_content || '',\n      });\n    }\n  }\n\n  log('Anthropic results:', results.length);\n  return results.slice(0, numResults);\n}\n\nasync function searchOpenAINative(query, numResults, modelConfig) {\n  const endpoint = normalizeEndpoint(modelConfig.baseUrl, '/responses');\n  const input = 'Search the web for: ' + query + '\\\\n\\\\nReturn up to ' + numResults + ' relevant results.';\n  const requestVariants = [\n    {\n      label: 'web_search',\n      body: {\n        model: modelConfig.model,\n        stream: false,\n        tools: [{ type: 'web_search' }],\n        tool_choice: 'required',\n        input: input,\n      },\n    },\n    {\n      label: 'web_search_preview',\n      body: {\n        model: modelConfig.model,\n        stream: false,\n        tools: [{ type: 'web_search_preview' }],\n        input: input,\n      },\n    },\n  ];\n\n  let lastError = null;\n  for (const variant of requestVariants) {\n    try {\n      log('OpenAI search:', query, '→', endpoint, '(' + variant.label + ')');\n      const response = await postJson(endpoint, {\n        'Content-Type': 'application/json',\n        Authorization: 'Bearer ' + modelConfig.apiKey,\n      }, variant.body);\n\n      const results = [];\n      const textBlocks = [];\n      for (const item of (response.output || [])) {\n        if (item.type !== 'message' || !Array.isArray(item.content)) continue;\n        for (const content of item.content) {\n          if (content.type !== 'output_text') continue;\n          if (content.text) {\n            textBlocks.push(content.text);\n          }\n          if (!Array.isArray(content.annotations)) continue;\n          for (const annotation of content.annotations) {\n            if (annotation.type !== 'url_citation' || !annotation.url) continue;\n            pushUniqueResult(results, {\n              title: annotation.title || '',\n              url: annotation.url || '',\n              content: annotation.title || '',\n            });\n          }\n        }\n      }\n\n      if (results.length === 0) {\n        for (const textBlock of textBlocks) {\n          for (const parsedResult of parseOpenAITextResults(textBlock, numResults)) {\n            pushUniqueResult(results, parsedResult);\n          }\n          if (results.length >= numResults) break;\n        }\n      }\n\n      log('OpenAI results:', results.length, 'via', variant.label);\n      return results.slice(0, numResults);\n    } catch (e) {\n      lastError = e;\n      log('OpenAI variant failed:', variant.label, '-', e.message);\n    }\n  }\n\n  throw lastError || new Error('OpenAI web search failed');\n}\n\nasync function search(query, numResults) {\n  numResults = numResults || 10;\n  log('Search:', query);\n\n  const resolved = getCurrentModelConfig(lastObservedProvider);\n  if (!resolved.modelConfig) {\n    return {\n      results: [],\n      source: 'none',\n      error: resolved.error,\n      statusCode: resolved.statusCode || 400,\n    };\n  }\n\n  try {\n    let results = [];\n    if (resolved.modelConfig.provider === 'anthropic') {\n      results = await searchAnthropicNative(query, numResults, resolved.modelConfig);\n    } else if (resolved.modelConfig.provider === 'openai') {\n      results = await searchOpenAINative(query, numResults, resolved.modelConfig);\n    } else {\n      return {\n        results: [],\n        source: 'none',\n        error: 'Unsupported provider: ' + resolved.modelConfig.provider,\n        statusCode: 400,\n      };\n    }\n\n    return {\n      results: results,\n      source: 'native-' + resolved.modelConfig.provider,\n      modelId: resolved.modelConfig.id,\n    };\n  } catch (e) {\n    return {\n      results: [],\n      source: 'none',\n      error: e && e.message ? e.message : String(e),\n      statusCode: 502,\n    };\n  }\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  const pathname = url.pathname;\n${generateFactoryCompatRequestGuard().trimEnd()}\n\n  if (pathname === '/health') {\n    writeJson(res, 200, { status: 'ok', mode: 'native-provider' });\n    return;\n  }\n\n  if (isSearchRequest(url, req.method)) {\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        if (result.error) {\n          log('Search failed:', result.error);\n          res.writeHead(result.statusCode || 500, { 'Content-Type': 'application/json' });\n          res.end(JSON.stringify({ error: result.error, results: [] }));\n          return;\n        }\n        log('Results:', result.results.length, 'from', result.source, 'model', result.modelId || 'unknown');\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${generateFactoryCompatRoutes().trimEnd()}\n\n  // Standalone mode: mock non-LLM APIs\n  if (process.env.STANDALONE_MODE === '1') {\n    const isCoreLLMApi = pathname.startsWith('/api/llm/a/') || pathname.startsWith('/api/llm/o/');\n\n    if (!isCoreLLMApi) {\n      if (pathname === '/api/sessions/create') {\n        writeJson(res, 200, { id: 'local-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10) });\n        return;\n      }\n      if (pathname === '/api/cli/whoami') {\n        writeJson(res, 401, { error: 'Unauthorized' });\n        return;\n      }\n      writeJson(res, 200, {});\n      return;\n    }\n  }\n\n  // Simple proxy - no SSE transformation (handled by proxy plugin)\n  if (url.pathname.startsWith('/api/llm/a/')) lastObservedProvider = 'anthropic';\n  if (url.pathname.startsWith('/api/llm/o/')) lastObservedProvider = 'openai';\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\"\nexport FACTORY_API_BASE_URL=\"http://127.0.0.1:$PORT\"\nexport FACTORY_APP_BASE_URL_OVERRIDE=\"http://127.0.0.1:$PORT\"\nexport FACTORY_APP_BASE_URL=\"http://127.0.0.1:$PORT\"\nexport TOOLS_WEBSEARCH_BASE_URL=\"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  skipLogin: boolean = false,\n  factoryCompat: boolean = false,\n): string {\n  const standaloneEnv = standalone ? \"STANDALONE_MODE=1 \" : \"\";\n  const skipLoginEnv = skipLogin ? \"DROID_SKIP_LOGIN=1 \" : \"\";\n  const factoryCompatEnv = factoryCompat ? \"DROID_FACTORY_COMPAT=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|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\nstart_proxy_supervisor() {\n  (\n    child_pid=\"\"\n    requested_port=\"\"\n    trap 'if [ -n \"$child_pid\" ] && kill -0 \"$child_pid\" 2>/dev/null; then kill \"$child_pid\" 2>/dev/null; wait \"$child_pid\" 2>/dev/null; fi; exit 0' INT TERM\n\n    while true; do\n      rm -f \"$PORT_FILE\"\n      if [ -n \"$DROID_SEARCH_DEBUG\" ]; then\n        ${standaloneEnv}${skipLoginEnv}${factoryCompatEnv}SEARCH_PROXY_PORT=\"\\${requested_port:-0}\" SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" 2>&1 &\n      else\n        ${standaloneEnv}${skipLoginEnv}${factoryCompatEnv}SEARCH_PROXY_PORT=\"\\${requested_port:-0}\" SEARCH_PROXY_PORT_FILE=\"$PORT_FILE\" node \"$PROXY_SCRIPT\" >/dev/null 2>&1 &\n      fi\n      child_pid=$!\n      wait \"$child_pid\"\n      exit_code=$?\n      child_pid=\"\"\n\n      if [ -z \"$requested_port\" ] && [ -f \"$PORT_FILE\" ]; then\n        requested_port=$(cat \"$PORT_FILE\" 2>/dev/null)\n      fi\n\n      [ -n \"$DROID_SEARCH_DEBUG\" ] && echo \"[websearch] Proxy exited (code: $exit_code); restarting...\" >&2\n      sleep 1\n    done\n  ) &\n  PROXY_PID=$!\n}\n\nstart_proxy_supervisor\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\")\n\nexport FACTORY_API_BASE_URL_OVERRIDE=\"http://127.0.0.1:$ACTUAL_PORT\"\nexport FACTORY_API_BASE_URL=\"http://127.0.0.1:$ACTUAL_PORT\"\nexport FACTORY_APP_BASE_URL_OVERRIDE=\"http://127.0.0.1:$ACTUAL_PORT\"\nexport FACTORY_APP_BASE_URL=\"http://127.0.0.1:$ACTUAL_PORT\"\nexport TOOLS_WEBSEARCH_BASE_URL=\"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  skipLogin: boolean = false,\n  factoryCompat: boolean = false,\n): string {\n  const standaloneEnv = standalone ? \"set STANDALONE_MODE=1\\r\\n\" : \"\";\n  const skipLoginEnv = skipLogin ? \"set DROID_SKIP_LOGIN=1\\r\\n\" : \"\";\n  const factoryCompatEnv = factoryCompat ? \"set DROID_FACTORY_COMPAT=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\"==\"plugin\" set \"PASSTHROUGH=1\"\n)\nif \"%PASSTHROUGH%\"==\"1\" (\n    \"%DROID_BIN%\" %*\n    exit /b %ERRORLEVEL%\n)\n\nREM Start proxy server\n${standaloneEnv}${skipLoginEnv}${factoryCompatEnv}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%\"\nset \"FACTORY_API_BASE_URL=http://127.0.0.1:%ACTUAL_PORT%\"\nset \"FACTORY_APP_BASE_URL_OVERRIDE=http://127.0.0.1:%ACTUAL_PORT%\"\nset \"FACTORY_APP_BASE_URL=http://127.0.0.1:%ACTUAL_PORT%\"\nset \"TOOLS_WEBSEARCH_BASE_URL=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  skipLogin: boolean = false,\n  factoryCompat: 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, skipLogin, factoryCompat)\n    : generateUnifiedWrapper(droidPath, proxyScriptPath, standalone, skipLogin, factoryCompat);\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';\nconst SEARCH_ROUTE_ALIASES = new Set(['/api/tools/web-search', '/api/tools/exa/search']);\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 isSearchRequest(url, method) {\n  return method === 'POST' && SEARCH_ROUTE_ALIASES.has(url.pathname);\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 (isSearchRequest(url, req.method)) {\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 * 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 { isManagedAliasTarget, pathExistsWithLstat } from \"./alias-utils.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 parseSemver(versionText: string): [number, number, number] | null {\n  const match = versionText.match(/^(\\d+)\\.(\\d+)\\.(\\d+)$/);\n  if (!match) return null;\n  return [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\nfunction compareSemver(left: string, right: string): number {\n  const l = parseSemver(left);\n  const r = parseSemver(right);\n  if (!l || !r) {\n    throw new Error(`Invalid semver comparison: \"${left}\" vs \"${right}\"`);\n  }\n\n  for (let i = 0; i < 3; i++) {\n    if (l[i] > r[i]) return 1;\n    if (l[i] < r[i]) return -1;\n  }\n  return 0;\n}\n\ninterface VersionedPatchRule {\n  id: string;\n  minVersion?: string;\n  maxVersion?: string; // Exclusive upper bound\n  buildPatches: () => Patch[];\n}\n\nfunction regexMatchesText(text: string, regex: RegExp): boolean {\n  const flags = regex.flags.includes(\"g\") ? regex.flags : `${regex.flags}g`;\n  return new RegExp(regex.source, flags).test(text);\n}\n\nfunction patchMatchesBinary(binaryBuffer: Buffer, binaryText: string, patch: Patch): boolean {\n  if (patch.regexPattern) {\n    return (\n      regexMatchesText(binaryText, patch.regexPattern) ||\n      (!!patch.alreadyPatchedRegexPattern &&\n        regexMatchesText(binaryText, patch.alreadyPatchedRegexPattern))\n    );\n  }\n\n  const variants = [\n    { pattern: patch.pattern, replacement: patch.replacement },\n    ...(patch.variants || []),\n  ];\n\n  return variants.some(\n    (variant) =>\n      (!!variant.pattern.length && binaryBuffer.includes(variant.pattern)) ||\n      (!!variant.replacement.length && binaryBuffer.includes(variant.replacement)),\n  );\n}\n\nfunction inferVersionedPatchRuleFromBinary(\n  droidPath: string | undefined,\n  rules: VersionedPatchRule[],\n): VersionedPatchRule | undefined {\n  if (!droidPath || !existsSync(droidPath)) {\n    return undefined;\n  }\n\n  try {\n    const binaryBuffer = readFileSync(droidPath);\n    const binaryText = binaryBuffer.toString(\"utf-8\");\n    const matchingRules = rules.filter((rule) =>\n      rule.buildPatches().every((patch) => patchMatchesBinary(binaryBuffer, binaryText, patch)),\n    );\n\n    if (matchingRules.length === 1) {\n      return matchingRules[0];\n    }\n  } catch {\n    // Fall through to the existing version-based errors below.\n  }\n\n  return undefined;\n}\n\nfunction matchesVersionRule(droidVersion: string, rule: VersionedPatchRule): boolean {\n  if (rule.minVersion && compareSemver(droidVersion, rule.minVersion) < 0) {\n    return false;\n  }\n  if (rule.maxVersion && compareSemver(droidVersion, rule.maxVersion) >= 0) {\n    return false;\n  }\n  return true;\n}\n\nfunction resolveVersionedPatches(\n  featureName: string,\n  droidVersion: string | undefined,\n  rules: VersionedPatchRule[],\n  droidPath?: string,\n): Patch[] {\n  if (droidVersion && parseSemver(droidVersion)) {\n    const matchedRule = rules.find((rule) => matchesVersionRule(droidVersion, rule));\n    if (matchedRule) {\n      return matchedRule.buildPatches();\n    }\n  }\n\n  const inferredRule = inferVersionedPatchRuleFromBinary(droidPath, rules);\n  if (inferredRule) {\n    return inferredRule.buildPatches();\n  }\n\n  if (!droidVersion) {\n    throw new Error(`Unable to detect droid version for ${featureName}`);\n  }\n  if (!parseSemver(droidVersion)) {\n    throw new Error(`Unsupported droid version format \"${droidVersion}\" for ${featureName}`);\n  }\n\n  const matchedRule = rules.find((rule) => matchesVersionRule(droidVersion, rule));\n  if (!matchedRule) {\n    throw new Error(`No patch rule matched ${featureName} on droid ${droidVersion}`);\n  }\n  return matchedRule.buildPatches();\n}\n\nconst SKIP_LOGIN_V068_PLUS_REGEX =\n  /process\\.env\\[[A-Za-z_$][A-Za-z0-9_$]*\\.FACTORY_API_KEY\\](?:\\?\\.trim\\(\\))?/g;\nconst SKIP_LOGIN_V068_PLUS_REPLACEMENT_PREFIX = \"fk-droid-patch-skip-\";\nconst SKIP_LOGIN_V068_PLUS_PATCHED_REGEX = /(?:\"fk-droid-patch-skip-[0-9]*\"|\"fk-skip-login\"[ ]*)/g;\n\nfunction createFixedLengthStringLiteral(prefix: string, targetLength: number): string {\n  if (targetLength < 2) {\n    throw new Error(`String literal target length must be at least 2, got ${targetLength}`);\n  }\n\n  const contentLength = targetLength - 2;\n  const content =\n    prefix.length >= contentLength\n      ? prefix.slice(0, contentLength)\n      : prefix.padEnd(contentLength, \"0\");\n\n  return `\"${content}\"`;\n}\n\nconst SKIP_LOGIN_PATCH_RULES: VersionedPatchRule[] = [\n  {\n    id: \"skip-login-legacy\",\n    maxVersion: \"0.68.0\",\n    buildPatches: () => [\n      {\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  {\n    id: \"skip-login-v068-plus\",\n    minVersion: \"0.68.0\",\n    buildPatches: () => [\n      {\n        name: \"factoryApiKeyLookupV068\",\n        description:\n          \"Replace process.env[<minified>.FACTORY_API_KEY]?.trim() with a fixed-length fake key via regex matching\",\n        pattern: Buffer.from(\"\"),\n        replacement: Buffer.from(\"\"),\n        regexPattern: SKIP_LOGIN_V068_PLUS_REGEX,\n        regexReplacement: (match) =>\n          createFixedLengthStringLiteral(SKIP_LOGIN_V068_PLUS_REPLACEMENT_PREFIX, match.length),\n        alreadyPatchedRegexPattern: SKIP_LOGIN_V068_PLUS_PATCHED_REGEX,\n      },\n    ],\n  },\n];\n\nconst INLINE_MODEL_PICKER_CALLBACK_REGEX =\n  /([A-Za-z$_][A-Za-z0-9$_]*)=[A-Za-z$_][A-Za-z0-9$_]*\\.useCallback\\(\\(\\)=>\\{if\\(([A-Za-z$_][A-Za-z0-9$_]*)\\.length<=1\\)return;let ([A-Za-z$_][A-Za-z0-9$_]*)=[A-Za-z$_][A-Za-z0-9$_]*\\(\\)\\.getModelPolicy\\(\\);if\\(!\\2\\.some\\(\\(([A-Za-z$_][A-Za-z0-9$_]*)\\)=>[A-Za-z$_][A-Za-z0-9$_]*\\(\\4,\\3\\)\\.allowed\\)\\)return;([A-Za-z$_][A-Za-z0-9$_]*)\\(\\(([A-Za-z$_][A-Za-z0-9$_]*)\\)=>!\\6\\)\\},\\[\\2\\]\\)/g;\nconst INLINE_MODEL_PICKER_INLINE_SETTER_REGEX =\n  /availableModels:([A-Za-z$_][A-Za-z0-9$_]*),currentModel:[^,]+,onSelect:[A-Za-z$_][A-Za-z0-9$_]*,onCancel:\\(\\)=>([A-Za-z$_][A-Za-z0-9$_]*)\\(!1\\)/g;\nconst INLINE_MODEL_PICKER_FULL_SELECTOR_REGEX =\n  /onCancel:\\(\\)=>\\{([A-Za-z$_][A-Za-z0-9$_]*)\\(!1\\),([A-Za-z$_][A-Za-z0-9$_]*)\\(!1\\),([A-Za-z$_][A-Za-z0-9$_]*)\\.current\\?\\.closeSuggestions\\?\\.\\(\\),\\3\\.current\\?\\.setInput\\?\\.\\(\"\"\\)\\}/g;\n\nfunction escapeRegExp(text: string): string {\n  return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction createInlineModelPickerSemanticPatch(binaryText: string): Patch | null {\n  const inlinePickerContexts = [\n    ...binaryText.matchAll(INLINE_MODEL_PICKER_INLINE_SETTER_REGEX),\n  ].map((match) => ({\n    modelsVar: match[1],\n    inlineSetter: match[2],\n  }));\n\n  if (inlinePickerContexts.length === 0) {\n    return null;\n  }\n\n  const callbackMatches = [...binaryText.matchAll(INLINE_MODEL_PICKER_CALLBACK_REGEX)].filter(\n    (match) =>\n      inlinePickerContexts.some(\n        (context) => context.modelsVar === match[2] && context.inlineSetter === match[5],\n      ),\n  );\n\n  if (callbackMatches.length !== 1) {\n    return null;\n  }\n\n  const callbackMatch = callbackMatches[0];\n  const inlineSetter = callbackMatch[5];\n  const fullSelectorSetters = [\n    ...new Set(\n      [...binaryText.matchAll(INLINE_MODEL_PICKER_FULL_SELECTOR_REGEX)]\n        .map((match) => match[1])\n        .filter((setter) => setter !== inlineSetter),\n    ),\n  ];\n\n  if (fullSelectorSetters.length !== 1) {\n    return null;\n  }\n\n  const fullSelectorSetter = fullSelectorSetters[0];\n  if (inlineSetter.length !== fullSelectorSetter.length) {\n    return null;\n  }\n\n  const callbackSource = callbackMatch[0];\n  const toggleParam = callbackMatch[6];\n  const inlineToggle = `${inlineSetter}((${toggleParam})=>!${toggleParam})`;\n  const fullSelectorToggle = `${fullSelectorSetter}((${toggleParam})=>!${toggleParam})`;\n\n  if (!callbackSource.includes(inlineToggle)) {\n    return null;\n  }\n\n  const callbackReplacement = callbackSource.replace(inlineToggle, fullSelectorToggle);\n  if (callbackReplacement === callbackSource) {\n    return null;\n  }\n\n  return {\n    name: \"inlineModelPickerUsesFullSelector\",\n    description:\n      \"Change ctrl+N callback from inline built-in picker to full model selector overlay\",\n    pattern: Buffer.from(\"\"),\n    replacement: Buffer.from(\"\"),\n    regexPattern: new RegExp(escapeRegExp(callbackSource), \"g\"),\n    regexReplacement: () => callbackReplacement,\n    alreadyPatchedRegexPattern: new RegExp(escapeRegExp(callbackReplacement), \"g\"),\n    suppressCheckUnlessFound: true,\n  };\n}\n\nfunction resolveInlineModelPickerPatch(droidPath: string | undefined): Patch | null {\n  if (!droidPath || !existsSync(droidPath)) {\n    return null;\n  }\n\n  try {\n    const binaryText = readFileSync(droidPath, \"utf-8\");\n    return createInlineModelPickerSemanticPatch(binaryText);\n  } catch {\n    return null;\n  }\n}\n\nconst FACTORYD_SELF_PATH_REGEX =\n  /if\\(([A-Za-z$_][A-Za-z0-9$_]*)\\.basename\\(process\\.execPath\\)\\.includes\\(\"droid\"\\)\\)/g;\nconst FACTORYD_SELF_PATH_PATCHED_REGEX =\n  /if\\(\\(1\\|\\|([A-Za-z$_][A-Za-z0-9$_]*)\\.basename\\(process\\.execPath\\)\\.includes\\(\"\"\\)\\)\\)/g;\nconst FACTORYD_SKIP_LOGIN_AUTH_REGEX =\n  /async function ([A-Za-z$_][A-Za-z0-9$_]*)\\(([A-Za-z$_][A-Za-z0-9$_]*)\\)\\{let ([A-Za-z$_][A-Za-z0-9$_]*)=([A-Za-z$_][A-Za-z0-9$_]*)\\(\\)\\.apiBaseUrl,([A-Za-z$_][A-Za-z0-9$_]*)=await fetch\\(`\\$\\{\\3\\}\\/api\\/cli\\/whoami`,\\{method:\"GET\",headers:\\{Authorization:`Bearer \\$\\{\\2\\}`\\}\\}\\),([A-Za-z$_][A-Za-z0-9$_]*)=await \\5\\.text\\(\\);if\\(!\\5\\.ok\\)throw new ([A-Za-z$_][A-Za-z0-9$_]*)\\(\"API key verification failed\",\\{statusCode:\\5\\.status,body:\\6\\}\\);let ([A-Za-z$_][A-Za-z0-9$_]*)=([A-Za-z$_][A-Za-z0-9$_]*)\\(\\6,([A-Za-z$_][A-Za-z0-9$_]*),\"whoami response\"\\);return\\{userId:\\8\\.userId,email:\"\",orgId:\\8\\.orgId\\}\\}/g;\nconst FACTORYD_SKIP_LOGIN_AUTH_PATCHED_REGEX =\n  /async function [A-Za-z$_][A-Za-z0-9$_]*\\(([A-Za-z$_][A-Za-z0-9$_]*)\\)\\{if\\(\\/\\^fk\\/\\.test\\(\\1\\)\\)return\\{userId:\"f\",orgId:\"f\"\\};let ([A-Za-z$_][A-Za-z0-9$_]*)=await fetch\\(`\\$\\{([A-Za-z$_][A-Za-z0-9$_]*)\\(\\)\\.apiBaseUrl\\}\\/api\\/cli\\/whoami`,\\{headers:\\{Authorization:`Bearer \\$\\{\\1\\}`\\}\\}\\);if\\(!\\2\\.ok\\)throw new [A-Za-z$_][A-Za-z0-9$_]*\\(\"API key verification failed\"\\);\\2=[A-Za-z$_][A-Za-z0-9$_]*\\(await \\2\\.text\\(\\),([A-Za-z$_][A-Za-z0-9$_]*),\"whoami response\"\\);return\\{userId:\\2\\.userId,email:\"\",orgId:\\2\\.orgId\\}\\s+\\}/g;\nconst MISSION_WORKER_EXIT_REGEX =\n  /if\\(([A-Za-z$_][A-Za-z0-9$_]*)\\(([A-Za-z$_][A-Za-z0-9$_]*)\\(\\)\\.getCurrentSessionTags\\(\\)\\)&&!this\\.wasInterrupted\\)([A-Za-z$_][A-Za-z0-9$_]*)\\(\"\\[JsonRpc\\] Worker session exiting after completing turn\"\\),await this\\.stop\\(\\),process\\.exit\\(0\\)/g;\nconst MISSION_WORKER_EXIT_PATCHED_REGEX =\n  /if\\(0\\)([A-Za-z$_][A-Za-z0-9$_]*)\\(\"\\[JsonRpc\\] Worker session exiting after completing turn\"\\),await this\\.stop\\(\\),process\\.exit\\(0\\)\\s*/g;\n\nfunction createMissionWorkerExitReplacement(match: string): string {\n  const captures = new RegExp(MISSION_WORKER_EXIT_REGEX.source).exec(match);\n  if (!captures) {\n    throw new Error(\"Failed to parse mission worker auto-exit match\");\n  }\n\n  const replacement = `if(0)${captures[3]}(\"[JsonRpc] Worker session exiting after completing turn\"),await this.stop(),process.exit(0)`;\n  return replacement.padEnd(match.length, \" \");\n}\n\nfunction createFactorydSelfPathPatch(): Patch {\n  return {\n    name: \"factorydSelfPath\",\n    description:\n      \"Force factoryd auto-start to reuse the current executable instead of falling back to plain droid\",\n    pattern: Buffer.from(\"\"),\n    replacement: Buffer.from(\"\"),\n    regexPattern: FACTORYD_SELF_PATH_REGEX,\n    regexReplacement: 'if((1||$1.basename(process.execPath).includes(\"\")))',\n    alreadyPatchedRegexPattern: FACTORYD_SELF_PATH_PATCHED_REGEX,\n  };\n}\n\nfunction createFactorydSkipLoginAuthPatch(): Patch {\n  return {\n    name: \"factorydSkipLoginAuth\",\n    description:\n      \"Allow mission/factoryd auth to reuse fk- API key sessions via the shared /api/cli/whoami helper\",\n    pattern: Buffer.from(\"\"),\n    replacement: Buffer.from(\"\"),\n    regexPattern: FACTORYD_SKIP_LOGIN_AUTH_REGEX,\n    regexReplacement:\n      'async function $1($2){if(/^fk/.test($2))return{userId:\"f\",orgId:\"f\"};let $3=await fetch(`${$4().apiBaseUrl}/api/cli/whoami`,{headers:{Authorization:`Bearer ${$2}`}});if(!$3.ok)throw new $7(\"API key verification failed\");$3=$9(await $3.text(),$10,\"whoami response\");return{userId:$3.userId,email:\"\",orgId:$3.orgId}        }',\n    alreadyPatchedRegexPattern: FACTORYD_SKIP_LOGIN_AUTH_PATCHED_REGEX,\n  };\n}\n\nfunction createMissionWorkerStayAlivePatch(): Patch {\n  return {\n    name: \"missionWorkerStayAlive\",\n    description:\n      \"Disable worker auto-exit after a completed turn so mission workers are not treated as crashed\",\n    pattern: Buffer.from(\"\"),\n    replacement: Buffer.from(\"\"),\n    regexPattern: MISSION_WORKER_EXIT_REGEX,\n    regexReplacement: createMissionWorkerExitReplacement,\n    alreadyPatchedRegexPattern: MISSION_WORKER_EXIT_PATCHED_REGEX,\n  };\n}\n\ntype BinaryPatchConfig = {\n  isCustom: boolean;\n  skipLogin: boolean;\n  apiBase: string | null | undefined;\n  websearch: boolean;\n  websearchProxy?: boolean;\n  reasoningEffort: boolean;\n  noTelemetry?: boolean;\n};\n\nfunction needsBinaryPatches(config: BinaryPatchConfig): boolean {\n  return (\n    config.isCustom ||\n    config.skipLogin ||\n    config.reasoningEffort ||\n    !!config.noTelemetry ||\n    (!!config.apiBase && !config.websearch && !config.websearchProxy)\n  );\n}\n\nfunction createMissionFactorydPatches(config: Pick<BinaryPatchConfig, \"skipLogin\">): Patch[] {\n  const patches = [createFactorydSelfPathPatch(), createMissionWorkerStayAlivePatch()];\n  if (config.skipLogin) {\n    patches.push(createFactorydSkipLoginAuthPatch());\n  }\n  return patches;\n}\n\nfunction requiresRuntimeProxy(config: Pick<BinaryPatchConfig, \"isCustom\" | \"skipLogin\">): boolean {\n  return config.isCustom || config.skipLogin;\n}\n\nfunction appendIsCustomPatches(patches: Patch[], droidPath: string | undefined): void {\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  const inlineModelPickerPatch = resolveInlineModelPickerPatch(droidPath);\n  if (inlineModelPickerPatch) {\n    patches.push(inlineModelPickerPatch);\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 PATH lookups first.\n  for (const lookupCommand of [\"command -v droid\", \"which droid\"]) {\n    try {\n      const result = execSync(lookupCommand, {\n        encoding: \"utf-8\",\n        stdio: [\"pipe\", \"pipe\", \"pipe\"],\n      }).trim();\n      const firstResult = result.split(/\\r?\\n/)[0];\n      if (firstResult && existsSync(firstResult)) {\n        return firstResult;\n      }\n    } catch {\n      // Continue to the next lookup strategy.\n    }\n  }\n\n  // Common installation paths (Unix)\n  const paths = [\n    // Default sh install location\n    join(home, \".droid\", \"bin\", \"droid\"),\n    // Common user-local install location\n    join(home, \".local\", \"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  .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 (default high; UI options: \"high\",\"max\",\"xhigh\")',\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.isCustom;\n    const skipLogin = !!options.skipLogin;\n    const apiBase = options.apiBase;\n    const websearch = !!options.websearch;\n    const websearchProxy = !!options.websearchProxy;\n    const standalone = !!options.standalone;\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.reasoningEffort;\n    const noTelemetry = !!options.disableTelemetry;\n    const dryRun = !!options.dryRun;\n    const path = options.path || findDefaultDroidPath();\n    const outputDir = options.output;\n    const backup = options.backup !== false;\n    const verbose = options.verbose as boolean;\n    const droidVersion = getDroidVersion(path);\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 = needsBinaryPatches({\n      isCustom: !!isCustom,\n      skipLogin: !!skipLogin,\n      apiBase,\n      websearch: !!websearch,\n      websearchProxy: !!websearchProxy,\n      reasoningEffort: !!reasoningEffort,\n      noTelemetry: !!noTelemetry,\n    });\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    const useRuntimeProxy = isWebsearchMode || requiresRuntimeProxy({ isCustom, skipLogin });\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        skipLogin,\n        false,\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 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[] = createMissionFactorydPatches({ skipLogin });\n    if (isCustom) {\n      appendIsCustomPatches(patches, path);\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      try {\n        patches.push(\n          ...resolveVersionedPatches(\"--skip-login\", droidVersion, SKIP_LOGIN_PATCH_RULES, path),\n        );\n      } catch (error) {\n        console.log(styleText(\"red\", `Error: ${(error as Error).message}`));\n        if (!droidVersion) {\n          console.log(styleText(\"gray\", \"Please use -p to point to a runnable droid binary.\"));\n        }\n        process.exit(1);\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      const 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 \"max\" and \"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: \"reasoningEffortSupportedXHighListLegacy\",\n        description: 'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model list (legacy)',\n        pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n        replacement: Buffer.from(\"\"),\n        suppressCheckUnlessFound: true,\n        regexPattern:\n          /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.(?:displayName|id),modelProvider:\\1\\.provider\\s*,supportedReasoningEfforts:\\[(?:\"(?:none|high)\"(?:,\"xhigh\")?|\"low\",\"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:[\"high\",\"max\",\"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:\\[\"high\",\"max\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n      });\n\n      patches.push({\n        name: \"reasoningEffortSupportedXHighResolverLegacy\",\n        description:\n          'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model config resolver (legacy)',\n        pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n        replacement: Buffer.from(\"\"),\n        suppressCheckUnlessFound: true,\n        regexPattern:\n          /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider\\s*,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:\\[(?:\"(?:none|high)\"(?:,\"xhigh\")?|\"low\",\"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:[\"high\",\"max\",\"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:\\[\"high\",\"max\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n      });\n\n      patches.push({\n        name: \"reasoningEffortSupportedXHighList\",\n        description: 'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model list',\n        pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n        replacement: Buffer.from(\"\"),\n        skipIfAnySucceeded: [\"reasoningEffortSupportedXHighListLegacy\"],\n        regexPattern:\n          /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.displayName,modelProvider:\\1\\.provider,supportedReasoningEfforts:[A-Za-z$_]\\?\\[\"off\",\"low\",\"medium\",\"high\"\\]:\\[\"none\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"none\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/g,\n        regexReplacement:\n          'id:$1.id,displayName:$1.displayName,shortDisplayName:$1.displayName,modelProvider:$1.provider,supportedReasoningEfforts:1?[\"high\",\"max\",\"xhigh\",\"none\"]:[\"high\"],defaultReasoningEffort:$1.reasoningEffort??\"high\",isCustom:!$2,noImageSupport:$1.noImageSupport',\n        alreadyPatchedRegexPattern:\n          /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.displayName,modelProvider:\\1\\.provider,supportedReasoningEfforts:1\\?\\[\"high\",\"max\",\"xhigh\"(?:,\"none\")?\\]\\s{0,7}:\\[\"high\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"high\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/g,\n      });\n\n      patches.push({\n        name: \"reasoningEffortSupportedXHighResolver\",\n        description: 'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model config resolver',\n        pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n        replacement: Buffer.from(\"\"),\n        skipIfAnySucceeded: [\"reasoningEffortSupportedXHighResolverLegacy\"],\n        regexPattern:\n          /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:[A-Za-z$_]\\?\\[\"off\",\"low\",\"medium\",\"high\"\\]:\\[\"none\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"none\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/g,\n        regexReplacement:\n          'id:$1.model,modelProvider:$1.provider,displayName:$2,shortDisplayName:$2,supportedReasoningEfforts:1?[\"high\",\"max\",\"xhigh\",\"none\"]:[\"high\"],defaultReasoningEffort:$1.reasoningEffort??\"high\",isCustom:!$3,noImageSupport:$1.noImageSupport',\n        alreadyPatchedRegexPattern:\n          /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:1\\?\\[\"high\",\"max\",\"xhigh\"(?:,\"none\")?\\]\\s{0,7}:\\[\"high\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"high\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/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(\n          styleText(\"gray\", \"To apply the patches, rerun the same command without --dry-run.\"),\n        );\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 (useRuntimeProxy) {\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            skipLogin,\n            requiresRuntimeProxy({ isCustom, skipLogin }),\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 if (websearch) {\n            console.log(styleText(\"cyan\", \"WebSearch enabled (external providers mode)\"));\n            console.log(styleText(\"white\", `  Forward target: ${websearchTarget}`));\n          } else {\n            console.log(\n              styleText(\"cyan\", \"Runtime proxy enabled for custom/skip-login session support\"),\n            );\n            console.log(\n              styleText(\n                \"gray\",\n                \"  Adds local Factory API compatibility shims needed by custom and mission worker flows\",\n              ),\n            );\n          }\n          if (standalone) {\n            console.log(styleText(\"white\", `  Standalone mode: enabled`));\n          }\n        }\n\n        let aliasResult;\n        if (useRuntimeProxy) {\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 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.patchVersion;\n    const droidVersion = options.droidVersion;\n    const flagRaw = options.flag;\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.dryRun;\n    const newBinaryPath = options.path || findDefaultDroidPath();\n    const verbose = !!options.verbose;\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    const newDroidVersion = getDroidVersion(newBinaryPath);\n    if (!newDroidVersion) {\n      console.log(\n        styleText(\"yellow\", \"[!] Warning: Could not detect droid version from the new binary\"),\n      );\n      console.log(styleText(\"gray\", \"    skip-login aliases may fail to update\"));\n    }\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    const aliasesRequiringRestart = new Set<string>();\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[] = needsBinaryPatches(meta.patches)\n          ? createMissionFactorydPatches({ skipLogin: meta.patches.skipLogin })\n          : [];\n\n        if (meta.patches.isCustom) {\n          appendIsCustomPatches(patches, newBinaryPath);\n        }\n\n        if (meta.patches.skipLogin) {\n          patches.push(\n            ...resolveVersionedPatches(\n              \"skip-login\",\n              newDroidVersion,\n              SKIP_LOGIN_PATCH_RULES,\n              newBinaryPath,\n            ),\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: \"reasoningEffortSupportedXHighListLegacy\",\n            description: 'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model list (legacy)',\n            pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n            replacement: Buffer.from(\"\"),\n            suppressCheckUnlessFound: true,\n            regexPattern:\n              /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.(?:displayName|id),modelProvider:\\1\\.provider\\s*,supportedReasoningEfforts:\\[(?:\"(?:none|high)\"(?:,\"xhigh\")?|\"low\",\"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:[\"high\",\"max\",\"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:\\[\"high\",\"max\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n          });\n          patches.push({\n            name: \"reasoningEffortSupportedXHighResolverLegacy\",\n            description:\n              'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model config resolver (legacy)',\n            pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n            replacement: Buffer.from(\"\"),\n            suppressCheckUnlessFound: true,\n            regexPattern:\n              /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider\\s*,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:\\[(?:\"(?:none|high)\"(?:,\"xhigh\")?|\"low\",\"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:[\"high\",\"max\",\"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:\\[\"high\",\"max\",\"xhigh\"\\],defaultReasoningEffort:\"high\",isCustom:!([01]),noImageSupport:!1/g,\n          });\n          patches.push({\n            name: \"reasoningEffortSupportedXHighList\",\n            description: 'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model list',\n            pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n            replacement: Buffer.from(\"\"),\n            skipIfAnySucceeded: [\"reasoningEffortSupportedXHighListLegacy\"],\n            regexPattern:\n              /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.displayName,modelProvider:\\1\\.provider,supportedReasoningEfforts:[A-Za-z$_]\\?\\[\"off\",\"low\",\"medium\",\"high\"\\]:\\[\"none\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"none\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/g,\n            regexReplacement:\n              'id:$1.id,displayName:$1.displayName,shortDisplayName:$1.displayName,modelProvider:$1.provider,supportedReasoningEfforts:1?[\"high\",\"max\",\"xhigh\",\"none\"]:[\"high\"],defaultReasoningEffort:$1.reasoningEffort??\"high\",isCustom:!$2,noImageSupport:$1.noImageSupport',\n            alreadyPatchedRegexPattern:\n              /id:([A-Za-z$_])\\.id,displayName:\\1\\.displayName,shortDisplayName:\\1\\.displayName,modelProvider:\\1\\.provider,supportedReasoningEfforts:1\\?\\[\"high\",\"max\",\"xhigh\"(?:,\"none\")?\\]\\s{0,7}:\\[\"high\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"high\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/g,\n          });\n          patches.push({\n            name: \"reasoningEffortSupportedXHighResolver\",\n            description: 'Enable [\"high\",\"max\",\"xhigh\"] in UI for custom model config resolver',\n            pattern: Buffer.from(\"\"), // Not used when regexPattern is set\n            replacement: Buffer.from(\"\"),\n            skipIfAnySucceeded: [\"reasoningEffortSupportedXHighResolverLegacy\"],\n            regexPattern:\n              /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:[A-Za-z$_]\\?\\[\"off\",\"low\",\"medium\",\"high\"\\]:\\[\"none\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"none\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/g,\n            regexReplacement:\n              'id:$1.model,modelProvider:$1.provider,displayName:$2,shortDisplayName:$2,supportedReasoningEfforts:1?[\"high\",\"max\",\"xhigh\",\"none\"]:[\"high\"],defaultReasoningEffort:$1.reasoningEffort??\"high\",isCustom:!$3,noImageSupport:$1.noImageSupport',\n            alreadyPatchedRegexPattern:\n              /id:([A-Za-z$_])\\.model,modelProvider:\\1\\.provider,displayName:([A-Za-z$_]),shortDisplayName:\\2,supportedReasoningEfforts:1\\?\\[\"high\",\"max\",\"xhigh\"(?:,\"none\")?\\]\\s{0,7}:\\[\"high\"\\],defaultReasoningEffort:\\1\\.reasoningEffort\\?\\?\"high\",isCustom:!([01]),noImageSupport:\\1\\.noImageSupport/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 =\n          meta.patches.websearch || !!meta.patches.websearchProxy || !!meta.patches.proxy;\n        const needsRuntimeProxy = requiresRuntimeProxy({\n          isCustom: meta.patches.isCustom,\n          skipLogin: meta.patches.skipLogin,\n        });\n        if (hasWebsearch || needsRuntimeProxy) {\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            meta.patches.websearchProxy || false,\n            meta.patches.skipLogin || false,\n            needsRuntimeProxy,\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          // Existing alias sessions keep using the proxy process that was started\n          // when the wrapper launched. Updating files on disk does not hot-reload\n          // those already-running proxy instances.\n          aliasesRequiringRestart.add(meta.name);\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 (pathExistsWithLstat(possiblePath)) {\n              try {\n                const stats = await lstat(possiblePath);\n                if (stats.isSymbolicLink()) {\n                  const target = await readlink(possiblePath);\n                  if (isManagedAliasTarget(target)) {\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 (pathExistsWithLstat(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 = newDroidVersion;\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    if (!dryRun && aliasesRequiringRestart.size > 0) {\n      const aliasList = [...aliasesRequiringRestart].join(\", \");\n      console.log();\n      console.log(\n        styleText(\"yellow\", `[!] Restart required for active runtime-proxy aliases: ${aliasList}`),\n      );\n      console.log(\n        styleText(\n          \"gray\",\n          \"    Existing sessions keep the old proxy process until that alias exits.\",\n        ),\n      );\n      console.log(\n        styleText(\n          \"gray\",\n          \"    Exit and relaunch those aliases before retesting mission startup or skip-login behavior.\",\n        ),\n      );\n    }\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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"}