one-search-mcp 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +7 -7
- package/dist/index.js +7 -7
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
2
|
+
"use strict";var Ee=Object.create;var F=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Te=Object.getOwnPropertyNames;var Ie=Object.getPrototypeOf,Ae=Object.prototype.hasOwnProperty;var K=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Te(e))!Ae.call(t,n)&&n!==r&&F(t,n,{get:()=>e[n],enumerable:!(s=ve(e,n))||s.enumerable});return t};var b=(t,e,r)=>(r=t!=null?Ee(Ie(t)):{},K(e||!t||!t.__esModule?F(r,"default",{value:t,enumerable:!0}):r,t)),Oe=t=>K(F({},"__esModule",{value:!0}),t);var gr={};module.exports=Oe(gr);var we=require("@modelcontextprotocol/sdk/server/mcp.js"),ye=require("@modelcontextprotocol/sdk/server/stdio.js");var i=require("zod/v3"),z=i.z.object({query:i.z.string().describe("Search query string"),limit:i.z.number().optional().describe("Maximum number of results to return (default: 10)"),language:i.z.string().optional().describe("Language code for search results (default: auto)"),categories:i.z.enum(["general","news","images","videos","it","science","map","music","files","social_media"]).optional().describe("Categories to search for (default: general)"),timeRange:i.z.enum(["all","day","week","month","year"]).optional().describe("Time range for search results (default: all)")}),W=i.z.object({url:i.z.string().describe("Starting URL for URL discovery"),search:i.z.string().optional().describe("Optional search term to filter URLs"),ignoreSitemap:i.z.boolean().optional().describe("Skip sitemap.xml discovery and only use HTML links"),sitemapOnly:i.z.boolean().optional().describe("Only use sitemap.xml for discovery, ignore HTML links"),includeSubdomains:i.z.boolean().optional().describe("Include URLs from subdomains in results"),limit:i.z.number().optional().describe("Maximum number of URLs to return")}),Pe=i.z.discriminatedUnion("type",[i.z.object({type:i.z.literal("wait"),milliseconds:i.z.number().describe("Time to wait in milliseconds")}),i.z.object({type:i.z.literal("click"),selector:i.z.string().describe("CSS selector for the target element")}),i.z.object({type:i.z.literal("screenshot"),fullPage:i.z.boolean().optional().describe("Take full page screenshot")}),i.z.object({type:i.z.literal("write"),selector:i.z.string().describe("CSS selector for the target element"),text:i.z.string().describe("Text to write")}),i.z.object({type:i.z.literal("press"),key:i.z.string().describe("Key to press")}),i.z.object({type:i.z.literal("scroll"),direction:i.z.enum(["up","down"]).describe("Scroll direction")}),i.z.object({type:i.z.literal("scrape")}),i.z.object({type:i.z.literal("executeJavascript"),script:i.z.string().describe("JavaScript code to execute")})]),V=i.z.object({url:i.z.string().describe("The URL to scrape"),formats:i.z.array(i.z.enum(["markdown","html","rawHtml","screenshot","links","screenshot@fullPage","extract"])).optional().describe("Content formats to extract (default: ['markdown'])"),onlyMainContent:i.z.boolean().optional().describe("Extract only the main content, filtering out navigation, footers, etc."),includeTags:i.z.array(i.z.string()).optional().describe("HTML tags to specifically include in extraction"),excludeTags:i.z.array(i.z.string()).optional().describe("HTML tags to exclude from extraction"),waitFor:i.z.number().optional().describe("Time in milliseconds to wait for dynamic content to load"),timeout:i.z.number().optional().describe("Maximum time in milliseconds to wait for the page to load"),actions:i.z.array(Pe).optional().describe("List of actions to perform before scraping"),extract:i.z.object({schema:i.z.record(i.z.any()).optional().describe("Schema for structured data extraction"),systemPrompt:i.z.string().optional().describe("System prompt for LLM extraction"),prompt:i.z.string().optional().describe("User prompt for LLM extraction")}).optional().describe("Configuration for structured data extraction"),mobile:i.z.boolean().optional().describe("Use mobile viewport"),skipTlsVerification:i.z.boolean().optional().describe("Skip TLS certificate verification"),removeBase64Images:i.z.boolean().optional().describe("Remove base64 encoded images from output"),location:i.z.object({country:i.z.string().optional().describe("Country code for geolocation"),languages:i.z.array(i.z.string()).optional().describe("Language codes for content")}).optional().describe("Location settings for scraping")}),Z=i.z.object({urls:i.z.array(i.z.string()).describe("List of URLs to extract information from")}).strict();var O={name:"one_search",description:"Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",schema:z},P={name:"one_map",description:"Discover URLs from a starting point. Can use both sitemap.xml and HTML link discovery.",schema:W},C={name:"one_scrape",description:"Scrape a single webpage with advanced options for content extraction. Supports various formats including markdown, HTML, and screenshots. Can execute custom actions like clicking or scrolling before scraping.",schema:V},_={name:"one_extract",description:"Fetch and preprocess page content from one or more URLs. Returns cleaned text blocks that can be passed to downstream tools or models.",schema:Z};var J=require("agent-browser/dist/browser.js"),X=b(require("turndown"),1),ee=require("turndown-plugin-gfm"),k=b(require("cheerio"),1);var L=b(require("fs"),1),N=b(require("path"),1),Q=b(require("os"),1);var Y=b(require("pino"),1),E=class{logger;constructor(e){this.logger=(0,Y.default)({name:e||"one-search-mcp",level:process.env.LOG_LEVEL||"info",transport:process.env.NODE_ENV==="development"?{target:"pino-pretty",options:{colorize:!0,translateTime:"SYS:standard",ignore:"pid,hostname"}}:void 0},process.stderr)}logWithLevel(e,r,...s){s.length>0?this.logger[e]({data:s},r):this.logger[e](r)}info(e,...r){this.logWithLevel("info",e,...r)}error(e,...r){this.logWithLevel("error",e,...r)}success(e,...r){r.length>0?this.logger.info({level:"success",data:r},e):this.logger.info({level:"success"},e)}warn(e,...r){this.logWithLevel("warn",e,...r)}log(e,...r){this.logWithLevel("info",e,...r)}},$=new E;var D=class{logger;constructor(e){this.logger=e??$}get browsers(){let e=Q.homedir(),r=process.env.LOCALAPPDATA;return[{name:"Chromium",executable:{win32:"C:\\Program Files\\Chromium\\Application\\chrome.exe",darwin:"/Applications/Chromium.app/Contents/MacOS/Chromium",linux:"/usr/bin/chromium"},userDataDir:{win32:`${r}\\Chromium\\User Data`,darwin:`${e}/Library/Application Support/Chromium`,linux:`${e}/.config/chromium`}},{name:"Google Chrome",executable:{win32:"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",darwin:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",linux:"/usr/bin/google-chrome"},userDataDir:{win32:`${r}\\Google\\Chrome\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome`,linux:`${e}/.config/google-chrome`}},{name:"Google Chrome Canary",executable:{win32:"C:\\Program Files\\Google\\Chrome Canary\\Application\\chrome.exe",darwin:"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",linux:"/usr/bin/google-chrome-canary"},userDataDir:{win32:`${r}\\Google\\Chrome Canary\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome Canary`,linux:`${e}/.config/google-chrome-canary`}},{name:"Microsoft Edge",executable:{win32:"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",darwin:"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",linux:"/usr/bin/microsoft-edge"},userDataDir:{win32:`${r}\\Microsoft\\Edge\\User Data`,darwin:`${e}/Library/Application Support/Microsoft Edge`,linux:`${e}/.config/microsoft-edge`}}]}findBrowser(e){let r=process.platform;if(this.logger.info("Finding browser on platform:",r),r!=="darwin"&&r!=="win32"&&r!=="linux"){let o=new Error(`Unsupported platform: ${r}`);throw this.logger.error(o.message),o}let s=e?this.browsers.find(o=>o.name===e&&L.existsSync(o.executable[r])):this.browsers.find(o=>L.existsSync(o.executable[r]));if(this.logger.log("browser",s),!s){let o=e?new Error(`Cannot find browser: ${e}`):new Error("Cannot find a supported browser on your system. Please install Chrome, Chromium, Edge, or Brave.");throw this.logger.error(o.message),o}let n={executable:s.executable[r],userDataDir:s.userDataDir[r]};return this.logger.success(`Found browser: ${s.name}`),this.logger.info("Browser details:",n),n}getBrowserProfiles(e){let r=this.findBrowser(e);try{let n=JSON.parse(L.readFileSync(N.join(r.userDataDir,"Local State"),"utf8")).profile.info_cache;return Object.entries(n).map(([o,c])=>({displayName:c.name,path:N.join(r.userDataDir,o)}))}catch{return[]}}findChrome(){try{let{executable:e}=this.findBrowser("Google Chrome");return e}catch{return null}}};var q={DEFAULT_WAIT_MS:2e3,SEARCH_WAIT_MS:3e3,DEFAULT_TIMEOUT:3e4,DEFAULT_HEADLESS:!0},j=["bing","google","baidu","sogou"];var x=class{constructor(e={}){this.options=e;this.browser=new J.BrowserManager;try{let r=new D,{executable:s}=r.findBrowser();this.browserPath=s}catch{this.browserPath=void 0}this.turndown=new X.default({headingStyle:"atx",codeBlockStyle:"fenced"}),this.turndown.use(ee.gfm)}browser;turndown;browserPath;async ensureLaunched(){if(!this.browser.isLaunched())try{await this.browser.launch({id:`session-${Date.now()}`,action:"launch",headless:this.options.headless??!0,executablePath:this.browserPath})}catch(e){let r=e instanceof Error?e.message:String(e);throw r.includes("Executable")||r.includes("browser")?new Error(`Browser not found. Please install one of the following:
|
|
3
3
|
- Google Chrome: https://www.google.com/chrome/
|
|
4
4
|
- Microsoft Edge: https://www.microsoft.com/edge
|
|
5
5
|
- Chromium: https://www.chromium.org/getting-involved/download-chromium/
|
|
6
6
|
Or install via Playwright:
|
|
7
7
|
npx playwright install chromium
|
|
8
|
-
Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(q.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(q.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(c=>{try{return new URL(c).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(q.SEARCH_WAIT_MS);let c=await this.getHtml();return this.extractSearchResults(s,c).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return $.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=k.load(e),s=[];return r("div.g").each((n,o)=>{let c=r(o),l=c.find("a[href]").first(),u=c.find("h3").first(),a=c.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=k.load(e),s=[];return r("li.b_algo").each((n,o)=>{let c=r(o),l=c.find("h2 a").first(),u=c.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=k.load(e),s=[];return r("div.result").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=k.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let c=new URL(o[1],r).href;n.includes(c)||n.push(c)}catch{}return n}};function ke(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var L=ke();function Le(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function te(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function v({browser:t,signal:e,task:r}){let s=L.track(t),n=Le(t,s);if(e?.aborted)throw await n(),te(e);let o;try{let c=r();if(!e)return await c;let l=new Promise((u,a)=>{o=()=>{n(),a(te(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([c,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}var H=require("duck-duck-scrape");var d=new R("search");function f({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var Me=2e4;async function se(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:c="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=f({signal:e,timeoutMs:Me,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([y,I])=>{I!==void 0&&h.set(y,I.toString())});let m=await fetch(`${c}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(y=>({title:y.name,snippet:y.snippet,url:y.url,source:y.siteName,thumbnailUrl:y.thumbnailUrl,language:y.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}var B=b(require("duck-duck-scrape"),1),ne=b(require("async-retry"),1);async function oe(t,e){let{query:r,timeout:s=1e4,safeSearch:n=B.SafeSearchType.OFF,retry:o={retries:3},...c}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await(0,ne.default)(async a=>{try{return await B.search(r,{...c,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}var ae=b(require("url"),1);async function ie(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:c="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let G=f({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let E={q:r,pageno:s,categories:o,format:u,safesearch:l,language:a,engines:c,time_range:p},U=`${m}/search`,y=ae.default.format({query:E}),I={"Content-Type":"application/json"};h&&(I.Authorization=`Bearer ${h}`);let K=await(await fetch(`${U}${y}`,{method:"POST",headers:I,signal:G.signal})).json();return K.results?{results:K.results.slice(0,n).map(S=>{let Ee=S.img_src?{thumbnail:S.thumbnail_src,src:S.img_src}:null,ve=S.iframe_src?{thumbnail:S.thumbnail_src,src:S.iframe_src}:null;return{title:S.title,snippet:S.content,url:S.url,source:S.source,image:Ee,video:ve,engine:S.engine}}),success:!0}:{results:[],success:!1}}catch(E){let U=E instanceof Error?E.message:"Searxng search error.";throw d.error(U),E}finally{G.cleanup()}}var ce=require("@tavily/core");var Ue=2e4,$e=new Set(["general","news","finance"]),De=new Set(["day","week","month","year","d","w","m","y"]);function qe(t){if(!(!t||!$e.has(t)))return t}function Be(t){if(!(!t||!De.has(t)))return t}function He(t){let e=Number(t);return!Number.isFinite(e)||e<=0?Math.ceil(Ue/1e3):Math.ceil(e/1e3)}async function le(t,e){let{query:r,limit:s=10,categories:n,timeRange:o,apiKey:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!c)throw new Error("Tavily API key is required");if(e?.aborted)throw e.reason instanceof Error?e.reason:new Error("Tavily search aborted");let l=(0,ce.tavily)({apiKey:c}),u={maxResults:s,timeout:He(t.timeout)},a=qe(n);a&&(u.topic=a);let p=Be(o);p&&(u.timeRange=p);try{return{results:(await l.search(r,u)).results.map(m=>({title:m.title,url:m.url,snippet:m.content,engine:"tavily"})),success:!0}}catch(g){let h=g instanceof Error?g.message:"Tavily search error.";throw d.error(h),g}}var T=new R("[LocalSearch]");function ue(t){return j.includes(t)}function Ge(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function pe(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let c=o.filter(ue),l=o.filter(a=>!ue(a));if(l.length>0&&T.warn(`Invalid search engines ignored: ${l.join(", ")}`),c.length===0)throw new Error(`No valid search engines provided. Valid engines: ${j.join(", ")}`);let u=new x({headless:!0,timeout:3e4});return await v({browser:u,signal:e,task:async()=>{let a=[];for(let p of c)try{T.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>Ge(m,p));a.push(...h),T.info(`Found ${g.length} results from ${p}`);break}}catch(g){T.error(`Failed to search with ${p}:`,g)}return T.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw T.error(p,a),a})}var Fe="https://www.googleapis.com/customsearch/v1",Ne=2e4;async function he(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});c&&c!=="auto"&&l.set("lr",`lang_${c}`);let u=f({signal:e,timeoutMs:Ne,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${Fe}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var je="https://open.bigmodel.cn/api/paas/v4/web_search",Ke=2e4;async function ge(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=f({signal:e,timeoutMs:Ke,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(je,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var ze="https://api.exa.ai/search",We=2e4,Ve=1e4;async function me(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=f({signal:e,timeoutMs:We,timeoutMessage:"Exa search timeout"});try{let c=await fetch(ze,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:Ve}}}),signal:o.signal});if(!c.ok)throw new Error(`Exa search failed: ${c.status} ${c.statusText}`);return{results:(await c.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(c){let l=c instanceof Error?c.message:"Exa search error.";throw d.error(l),c}finally{o.cleanup()}}var Ze="https://api.bocha.cn/v1/web-search",Ye=2e4;async function de(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let c="noLimit";o&&(c={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=f({signal:e,timeoutMs:Ye,timeoutMessage:"Bocha search timeout"});try{let u=await fetch(Ze,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:c}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var Qe={searxngSearch:async(t,e)=>await ie(t,e),tavilySearch:async(t,e)=>await le(t,e),bingSearch:async(t,e)=>await se(t,e),duckDuckGoSearch:async(t,e)=>await oe(t,e),localSearch:async(t,e)=>await pe(t,e),googleSearch:async(t,e)=>await he(t,e),zhipuSearch:async(t,e)=>await ge(t,e),exaSearch:async(t,e)=>await me(t,e),bochaSearch:async(t,e)=>await de(t,e)};async function fe(t,e,r,s=Qe){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey},{categories:o,language:c}=e.defaultSearchOptions;return o&&(n.categories=o),c&&(n.language=c),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[H.SafeSearchType.STRICT,H.SafeSearchType.MODERATE,H.SafeSearchType.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}async function Se(t,e){let r=[];for(let s of t)try{let n=await e.scrapeUrl(s,{formats:["markdown"],onlyMainContent:!0});if(n.success&&n.markdown){r.push(`## Content from ${s}
|
|
8
|
+
Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(q.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(q.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(c=>{try{return new URL(c).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(q.SEARCH_WAIT_MS);let c=await this.getHtml();return this.extractSearchResults(s,c).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return $.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=k.load(e),s=[];return r("div.g").each((n,o)=>{let c=r(o),l=c.find("a[href]").first(),u=c.find("h3").first(),a=c.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=k.load(e),s=[];return r("li.b_algo").each((n,o)=>{let c=r(o),l=c.find("h2 a").first(),u=c.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=k.load(e),s=[];return r("div.result").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=k.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let c=new URL(o[1],r).href;n.includes(c)||n.push(c)}catch{}return n}};function Ce(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var M=Ce();function _e(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function re(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function T({browser:t,signal:e,task:r}){let s=M.track(t),n=_e(t,s);if(e?.aborted)throw await n(),re(e);let o;try{let c=r();if(!e)return await c;let l=new Promise((u,a)=>{o=()=>{n(),a(re(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([c,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}var H=require("duck-duck-scrape");var d=new E("search");function S({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var Le=2e4;async function te(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:c="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=S({signal:e,timeoutMs:Le,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([f,v])=>{v!==void 0&&h.set(f,v.toString())});let m=await fetch(`${c}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(f=>({title:f.name,snippet:f.snippet,url:f.url,source:f.siteName,thumbnailUrl:f.thumbnailUrl,language:f.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}var B=b(require("duck-duck-scrape"),1),se=b(require("async-retry"),1);async function ne(t,e){let{query:r,timeout:s=1e4,safeSearch:n=B.SafeSearchType.OFF,retry:o={retries:3},...c}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await(0,se.default)(async a=>{try{return await B.search(r,{...c,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}var ke=new Set(["day","month","year"]);async function oe(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:c="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let G=S({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let R=new URLSearchParams({q:r,pageno:String(s),categories:o,format:u,safesearch:String(l),language:a,engines:c});ke.has(p)&&R.set("time_range",p);let A={};h&&(A.Authorization=`Bearer ${h}`);let f=await fetch(`${m}/search?${R.toString()}`,{method:"GET",headers:A,signal:G.signal});if(!f.ok)throw new Error(`SearxNG search failed: ${f.status} ${f.statusText}`);let v;try{v=await f.json()}catch{throw new Error("SearxNG search returned invalid JSON response")}return v.results?{results:v.results.slice(0,n).map(w=>{let xe=w.img_src?{thumbnail:w.thumbnail_src,src:w.img_src}:null,Re=w.iframe_src?{thumbnail:w.thumbnail_src,src:w.iframe_src}:null;return{title:w.title,snippet:w.content,url:w.url,source:w.source,image:xe,video:Re,engine:w.engine}}),success:!0}:{results:[],success:!1}}catch(R){let A=R instanceof Error?R.message:"Searxng search error.";throw d.error(A),R}finally{G.cleanup()}}var ae=require("@tavily/core");var Me=2e4,Ue=new Set(["general","news","finance"]),$e=new Set(["day","week","month","year","d","w","m","y"]);function De(t){if(!(!t||!Ue.has(t)))return t}function qe(t){if(!(!t||!$e.has(t)))return t}function Be(t){let e=Number(t);return!Number.isFinite(e)||e<=0?Math.ceil(Me/1e3):Math.ceil(e/1e3)}async function ie(t,e){let{query:r,limit:s=10,categories:n,timeRange:o,apiKey:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!c)throw new Error("Tavily API key is required");if(e?.aborted)throw e.reason instanceof Error?e.reason:new Error("Tavily search aborted");let l=(0,ae.tavily)({apiKey:c}),u={maxResults:s,timeout:Be(t.timeout)},a=De(n);a&&(u.topic=a);let p=qe(o);p&&(u.timeRange=p);try{return{results:(await l.search(r,u)).results.map(m=>({title:m.title,url:m.url,snippet:m.content,engine:"tavily"})),success:!0}}catch(g){let h=g instanceof Error?g.message:"Tavily search error.";throw d.error(h),g}}var I=new E("[LocalSearch]");function ce(t){return j.includes(t)}function He(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function le(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let c=o.filter(ce),l=o.filter(a=>!ce(a));if(l.length>0&&I.warn(`Invalid search engines ignored: ${l.join(", ")}`),c.length===0)throw new Error(`No valid search engines provided. Valid engines: ${j.join(", ")}`);let u=new x({headless:!0,timeout:3e4});return await T({browser:u,signal:e,task:async()=>{let a=[];for(let p of c)try{I.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>He(m,p));a.push(...h),I.info(`Found ${g.length} results from ${p}`);break}}catch(g){I.error(`Failed to search with ${p}:`,g)}return I.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw I.error(p,a),a})}var Ge="https://www.googleapis.com/customsearch/v1",Fe=2e4;async function ue(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});c&&c!=="auto"&&l.set("lr",`lang_${c}`);let u=S({signal:e,timeoutMs:Fe,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${Ge}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var Ne="https://open.bigmodel.cn/api/paas/v4/web_search",je=2e4;async function pe(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=S({signal:e,timeoutMs:je,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(Ne,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var Ke="https://api.exa.ai/search",ze=2e4,We=1e4;async function he(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=S({signal:e,timeoutMs:ze,timeoutMessage:"Exa search timeout"});try{let c=await fetch(Ke,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:We}}}),signal:o.signal});if(!c.ok)throw new Error(`Exa search failed: ${c.status} ${c.statusText}`);return{results:(await c.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(c){let l=c instanceof Error?c.message:"Exa search error.";throw d.error(l),c}finally{o.cleanup()}}var Ve="https://api.bocha.cn/v1/web-search",Ze=2e4;async function ge(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let c="noLimit";o&&(c={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=S({signal:e,timeoutMs:Ze,timeoutMessage:"Bocha search timeout"});try{let u=await fetch(Ve,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:c}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var Ye={searxngSearch:async(t,e)=>await oe(t,e),tavilySearch:async(t,e)=>await ie(t,e),bingSearch:async(t,e)=>await te(t,e),duckDuckGoSearch:async(t,e)=>await ne(t,e),localSearch:async(t,e)=>await le(t,e),googleSearch:async(t,e)=>await ue(t,e),zhipuSearch:async(t,e)=>await pe(t,e),exaSearch:async(t,e)=>await he(t,e),bochaSearch:async(t,e)=>await ge(t,e)};async function me(t,e,r,s=Ye){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},{categories:o,language:c}=e.defaultSearchOptions;return o&&(n.categories=o),c&&(n.language=c),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[H.SafeSearchType.STRICT,H.SafeSearchType.MODERATE,H.SafeSearchType.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}async function de(t,e){let r=[];for(let s of t)try{let n=await e.scrapeUrl(s,{formats:["markdown"],onlyMainContent:!0});if(n.success&&n.markdown){r.push(`## Content from ${s}
|
|
9
9
|
|
|
10
10
|
${n.markdown}`);continue}r.push(`## Failed to extract from ${s}
|
|
11
11
|
|
|
@@ -15,15 +15,15 @@ Error: ${o}`)}return r.join(`
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
`)}function
|
|
18
|
+
`)}function U(t,e,r={}){return async(s,n)=>{let o=Date.now();try{r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request started for tool: [${t}]`});let c=await e(s,n);return r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request completed in ${Date.now()-o}ms`}),c}catch(c){return r.logMessage&&await r.logMessage({level:"error",data:`[${new Date().toISOString()}] Error in ${t}: ${c}`}),{content:[{type:"text",text:c instanceof Error?c.message:String(c)}],isError:!0}}}}function fe(t,e){let r,s=async()=>{r||(r=e()),await r},n=o=>{t.once(o,()=>{s().finally(()=>{typeof t.kill=="function"&&typeof t.pid=="number"&&t.kill(t.pid,o)})})};t.once("beforeExit",()=>{s()}),n("SIGINT"),n("SIGTERM")}function Se(t,e,r){let s,n=!1,o=u=>{n||(n=!0,r(u))},c=async()=>{s||(s=e()),await s},l=()=>{c().then(()=>{o(0)}).catch(()=>{o(1)})};t.once("end",l),t.once("close",l)}var be=b(require("@dotenvx/dotenvx"),1);be.default.config({quiet:!0});var Qe=process.env.SEARCH_API_URL,Je=process.env.SEARCH_API_KEY,Xe=process.env.SEARCH_PROVIDER??"local",er=process.env.SAFE_SEARCH??0,rr=process.env.LIMIT??10,tr=process.env.CATEGORIES??"general",sr=process.env.ENGINES??"all",nr=process.env.FORMAT??"json",or=process.env.LANGUAGE??"auto",ar=process.env.TIME_RANGE??"",ir=process.env.TIMEOUT??1e4,y=new we.McpServer({name:"one-search-mcp",version:"1.2.0"},{capabilities:{tools:{},logging:{}}}),cr={limit:Number(rr),categories:tr,format:nr,safeSearch:Number(er),language:or,engines:sr,timeRange:ar,timeout:ir};fe(process,async()=>{await M.cleanup()});Se(process.stdin,async()=>{await M.cleanup(),await y.close()},t=>{process.exit(t)});y.registerTool(O.name,{description:O.description,inputSchema:O.schema},U(O.name,async(t,e)=>{let{results:r,success:s}=await me(t,{provider:Xe,apiKey:Je??"",apiUrl:Qe,defaultSearchOptions:cr},e.signal);if(!s)throw new Error("Failed to search");return{content:[{type:"text",text:r.map(o=>`Title: ${o.title}
|
|
19
19
|
URL: ${o.url}
|
|
20
20
|
Description: ${o.snippet}
|
|
21
21
|
${o.markdown?`Content: ${o.markdown}`:""}`).join(`
|
|
22
22
|
|
|
23
|
-
`)}]}},{logMessage:t=>
|
|
23
|
+
`)}]}},{logMessage:t=>y.sendLoggingMessage(t)}));y.registerTool(C.name,{description:C.description,inputSchema:C.schema},U(C.name,async(t,e)=>{let{url:r,...s}=t,{content:n}=await lr(r,s,e.signal);return{content:n}},{logMessage:t=>y.sendLoggingMessage(t)}));y.registerTool(P.name,{description:P.description,inputSchema:P.schema},U(P.name,async(t,e)=>{let{content:r}=await ur(t.url,t,e.signal);return{content:r}},{logMessage:t=>y.sendLoggingMessage(t)}));y.registerTool(_.name,{description:_.description,inputSchema:_.schema},U(_.name,async(t,e)=>{let{content:r}=await pr(t,e.signal);return{content:r}},{logMessage:t=>y.sendLoggingMessage(t)}));async function lr(t,e,r){let s=new x({headless:!0,timeout:3e4});return await T({browser:s,signal:r,task:async()=>{let n=await s.scrapeUrl(t,e);if(!n.success)throw new Error(`Failed to scrape: ${n.error}`);let o=[];return n.markdown&&o.push(n.markdown),n.rawHtml&&o.push(n.rawHtml),n.links&&o.push(n.links.join(`
|
|
24
24
|
`)),n.screenshot&&o.push(n.screenshot),n.html&&o.push(n.html),{content:[{type:"text",text:o.join(`
|
|
25
25
|
|
|
26
|
-
`)||"No content found"}],result:n,success:!0}}})}async function
|
|
27
|
-
`).trim()}],result:n.links,success:!0}}})}async function
|
|
28
|
-
`),process.exit(1)}}
|
|
26
|
+
`)||"No content found"}],result:n,success:!0}}})}async function ur(t,e,r){let s=new x({headless:!0,timeout:3e4});return await T({browser:s,signal:r,task:async()=>{let n=await s.mapUrl(t,e);if(!n.success)throw new Error(`Failed to map: ${n.error}`);if(!n.links)throw new Error(`No links found from: ${t}`);return{content:[{type:"text",text:n.links.join(`
|
|
27
|
+
`).trim()}],result:n.links,success:!0}}})}async function pr(t,e){let{urls:r}=t,s=new x({headless:!0,timeout:3e4});return await T({browser:s,signal:e,task:async()=>({content:[{type:"text",text:await de(r,s)}]})})}async function hr(){try{let t=new ye.StdioServerTransport;await y.connect(t),await y.sendLoggingMessage({level:"info",data:"OneSearch MCP server started"})}catch(t){let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error starting server: ${e}
|
|
28
|
+
`),process.exit(1)}}hr().catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error running server: ${e}
|
|
29
29
|
`),process.exit(1)});
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{McpServer as
|
|
2
|
+
import{McpServer as Be}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as He}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as c}from"zod/v3";var N=c.object({query:c.string().describe("Search query string"),limit:c.number().optional().describe("Maximum number of results to return (default: 10)"),language:c.string().optional().describe("Language code for search results (default: auto)"),categories:c.enum(["general","news","images","videos","it","science","map","music","files","social_media"]).optional().describe("Categories to search for (default: general)"),timeRange:c.enum(["all","day","week","month","year"]).optional().describe("Time range for search results (default: all)")}),j=c.object({url:c.string().describe("Starting URL for URL discovery"),search:c.string().optional().describe("Optional search term to filter URLs"),ignoreSitemap:c.boolean().optional().describe("Skip sitemap.xml discovery and only use HTML links"),sitemapOnly:c.boolean().optional().describe("Only use sitemap.xml for discovery, ignore HTML links"),includeSubdomains:c.boolean().optional().describe("Include URLs from subdomains in results"),limit:c.number().optional().describe("Maximum number of URLs to return")}),pe=c.discriminatedUnion("type",[c.object({type:c.literal("wait"),milliseconds:c.number().describe("Time to wait in milliseconds")}),c.object({type:c.literal("click"),selector:c.string().describe("CSS selector for the target element")}),c.object({type:c.literal("screenshot"),fullPage:c.boolean().optional().describe("Take full page screenshot")}),c.object({type:c.literal("write"),selector:c.string().describe("CSS selector for the target element"),text:c.string().describe("Text to write")}),c.object({type:c.literal("press"),key:c.string().describe("Key to press")}),c.object({type:c.literal("scroll"),direction:c.enum(["up","down"]).describe("Scroll direction")}),c.object({type:c.literal("scrape")}),c.object({type:c.literal("executeJavascript"),script:c.string().describe("JavaScript code to execute")})]),K=c.object({url:c.string().describe("The URL to scrape"),formats:c.array(c.enum(["markdown","html","rawHtml","screenshot","links","screenshot@fullPage","extract"])).optional().describe("Content formats to extract (default: ['markdown'])"),onlyMainContent:c.boolean().optional().describe("Extract only the main content, filtering out navigation, footers, etc."),includeTags:c.array(c.string()).optional().describe("HTML tags to specifically include in extraction"),excludeTags:c.array(c.string()).optional().describe("HTML tags to exclude from extraction"),waitFor:c.number().optional().describe("Time in milliseconds to wait for dynamic content to load"),timeout:c.number().optional().describe("Maximum time in milliseconds to wait for the page to load"),actions:c.array(pe).optional().describe("List of actions to perform before scraping"),extract:c.object({schema:c.record(c.any()).optional().describe("Schema for structured data extraction"),systemPrompt:c.string().optional().describe("System prompt for LLM extraction"),prompt:c.string().optional().describe("User prompt for LLM extraction")}).optional().describe("Configuration for structured data extraction"),mobile:c.boolean().optional().describe("Use mobile viewport"),skipTlsVerification:c.boolean().optional().describe("Skip TLS certificate verification"),removeBase64Images:c.boolean().optional().describe("Remove base64 encoded images from output"),location:c.object({country:c.string().optional().describe("Country code for geolocation"),languages:c.array(c.string()).optional().describe("Language codes for content")}).optional().describe("Location settings for scraping")}),z=c.object({urls:c.array(c.string()).describe("List of URLs to extract information from")}).strict();var A={name:"one_search",description:"Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",schema:N},O={name:"one_map",description:"Discover URLs from a starting point. Can use both sitemap.xml and HTML link discovery.",schema:j},P={name:"one_scrape",description:"Scrape a single webpage with advanced options for content extraction. Supports various formats including markdown, HTML, and screenshots. Can execute custom actions like clicking or scrolling before scraping.",schema:K},C={name:"one_extract",description:"Fetch and preprocess page content from one or more URLs. Returns cleaned text blocks that can be passed to downstream tools or models.",schema:z};import{BrowserManager as ge}from"agent-browser/dist/browser.js";import me from"turndown";import{gfm as de}from"turndown-plugin-gfm";import*as L from"cheerio";import*as _ from"fs";import*as H from"path";import*as W from"os";import he from"pino";var R=class{logger;constructor(e){this.logger=he({name:e||"one-search-mcp",level:process.env.LOG_LEVEL||"info",transport:process.env.NODE_ENV==="development"?{target:"pino-pretty",options:{colorize:!0,translateTime:"SYS:standard",ignore:"pid,hostname"}}:void 0},process.stderr)}logWithLevel(e,r,...s){s.length>0?this.logger[e]({data:s},r):this.logger[e](r)}info(e,...r){this.logWithLevel("info",e,...r)}error(e,...r){this.logWithLevel("error",e,...r)}success(e,...r){r.length>0?this.logger.info({level:"success",data:r},e):this.logger.info({level:"success"},e)}warn(e,...r){this.logWithLevel("warn",e,...r)}log(e,...r){this.logWithLevel("info",e,...r)}},U=new R;var $=class{logger;constructor(e){this.logger=e??U}get browsers(){let e=W.homedir(),r=process.env.LOCALAPPDATA;return[{name:"Chromium",executable:{win32:"C:\\Program Files\\Chromium\\Application\\chrome.exe",darwin:"/Applications/Chromium.app/Contents/MacOS/Chromium",linux:"/usr/bin/chromium"},userDataDir:{win32:`${r}\\Chromium\\User Data`,darwin:`${e}/Library/Application Support/Chromium`,linux:`${e}/.config/chromium`}},{name:"Google Chrome",executable:{win32:"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",darwin:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",linux:"/usr/bin/google-chrome"},userDataDir:{win32:`${r}\\Google\\Chrome\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome`,linux:`${e}/.config/google-chrome`}},{name:"Google Chrome Canary",executable:{win32:"C:\\Program Files\\Google\\Chrome Canary\\Application\\chrome.exe",darwin:"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",linux:"/usr/bin/google-chrome-canary"},userDataDir:{win32:`${r}\\Google\\Chrome Canary\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome Canary`,linux:`${e}/.config/google-chrome-canary`}},{name:"Microsoft Edge",executable:{win32:"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",darwin:"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",linux:"/usr/bin/microsoft-edge"},userDataDir:{win32:`${r}\\Microsoft\\Edge\\User Data`,darwin:`${e}/Library/Application Support/Microsoft Edge`,linux:`${e}/.config/microsoft-edge`}}]}findBrowser(e){let r=process.platform;if(this.logger.info("Finding browser on platform:",r),r!=="darwin"&&r!=="win32"&&r!=="linux"){let o=new Error(`Unsupported platform: ${r}`);throw this.logger.error(o.message),o}let s=e?this.browsers.find(o=>o.name===e&&_.existsSync(o.executable[r])):this.browsers.find(o=>_.existsSync(o.executable[r]));if(this.logger.log("browser",s),!s){let o=e?new Error(`Cannot find browser: ${e}`):new Error("Cannot find a supported browser on your system. Please install Chrome, Chromium, Edge, or Brave.");throw this.logger.error(o.message),o}let n={executable:s.executable[r],userDataDir:s.userDataDir[r]};return this.logger.success(`Found browser: ${s.name}`),this.logger.info("Browser details:",n),n}getBrowserProfiles(e){let r=this.findBrowser(e);try{let n=JSON.parse(_.readFileSync(H.join(r.userDataDir,"Local State"),"utf8")).profile.info_cache;return Object.entries(n).map(([o,i])=>({displayName:i.name,path:H.join(r.userDataDir,o)}))}catch{return[]}}findChrome(){try{let{executable:e}=this.findBrowser("Google Chrome");return e}catch{return null}}};var D={DEFAULT_WAIT_MS:2e3,SEARCH_WAIT_MS:3e3,DEFAULT_TIMEOUT:3e4,DEFAULT_HEADLESS:!0},G=["bing","google","baidu","sogou"];var b=class{constructor(e={}){this.options=e;this.browser=new ge;try{let r=new $,{executable:s}=r.findBrowser();this.browserPath=s}catch{this.browserPath=void 0}this.turndown=new me({headingStyle:"atx",codeBlockStyle:"fenced"}),this.turndown.use(de)}browser;turndown;browserPath;async ensureLaunched(){if(!this.browser.isLaunched())try{await this.browser.launch({id:`session-${Date.now()}`,action:"launch",headless:this.options.headless??!0,executablePath:this.browserPath})}catch(e){let r=e instanceof Error?e.message:String(e);throw r.includes("Executable")||r.includes("browser")?new Error(`Browser not found. Please install one of the following:
|
|
3
3
|
- Google Chrome: https://www.google.com/chrome/
|
|
4
4
|
- Microsoft Edge: https://www.microsoft.com/edge
|
|
5
5
|
- Chromium: https://www.chromium.org/getting-involved/download-chromium/
|
|
6
6
|
Or install via Playwright:
|
|
7
7
|
npx playwright install chromium
|
|
8
|
-
Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(D.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(D.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(i=>{try{return new URL(i).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(D.SEARCH_WAIT_MS);let i=await this.getHtml();return this.extractSearchResults(s,i).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return U.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=_.load(e),s=[];return r("div.g").each((n,o)=>{let i=r(o),l=i.find("a[href]").first(),u=i.find("h3").first(),a=i.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=_.load(e),s=[];return r("li.b_algo").each((n,o)=>{let i=r(o),l=i.find("h2 a").first(),u=i.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=_.load(e),s=[];return r("div.result").each((n,o)=>{let i=r(o),l=i.find("h3 a").first(),u=i.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=_.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let i=r(o),l=i.find("h3 a").first(),u=i.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let i=new URL(o[1],r).href;n.includes(i)||n.push(i)}catch{}return n}};function Se(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var k=Se();function we(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function Z(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function E({browser:t,signal:e,task:r}){let s=k.track(t),n=we(t,s);if(e?.aborted)throw await n(),Z(e);let o;try{let i=r();if(!e)return await i;let l=new Promise((u,a)=>{o=()=>{n(),a(Z(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([i,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}import{SafeSearchType as F}from"duck-duck-scrape";var d=new x("search");function f({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var ye=2e4;async function Y(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:i="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=f({signal:e,timeoutMs:ye,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([y,T])=>{T!==void 0&&h.set(y,T.toString())});let m=await fetch(`${i}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(y=>({title:y.name,snippet:y.snippet,url:y.url,source:y.siteName,thumbnailUrl:y.thumbnailUrl,language:y.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}import*as q from"duck-duck-scrape";import be from"async-retry";async function Q(t,e){let{query:r,timeout:s=1e4,safeSearch:n=q.SafeSearchType.OFF,retry:o={retries:3},...i}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await be(async a=>{try{return await q.search(r,{...i,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}import xe from"url";async function J(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:i="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let B=f({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let R={q:r,pageno:s,categories:o,format:u,safesearch:l,language:a,engines:i,time_range:p},M=`${m}/search`,y=xe.format({query:R}),T={"Content-Type":"application/json"};h&&(T.Authorization=`Bearer ${h}`);let N=await(await fetch(`${M}${y}`,{method:"POST",headers:T,signal:B.signal})).json();return N.results?{results:N.results.slice(0,n).map(S=>{let ue=S.img_src?{thumbnail:S.thumbnail_src,src:S.img_src}:null,pe=S.iframe_src?{thumbnail:S.thumbnail_src,src:S.iframe_src}:null;return{title:S.title,snippet:S.content,url:S.url,source:S.source,image:ue,video:pe,engine:S.engine}}),success:!0}:{results:[],success:!1}}catch(R){let M=R instanceof Error?R.message:"Searxng search error.";throw d.error(M),R}finally{B.cleanup()}}import{tavily as Re}from"@tavily/core";var Ee=2e4,ve=new Set(["general","news","finance"]),Te=new Set(["day","week","month","year","d","w","m","y"]);function Ie(t){if(!(!t||!ve.has(t)))return t}function Oe(t){if(!(!t||!Te.has(t)))return t}function Ae(t){let e=Number(t);return!Number.isFinite(e)||e<=0?Math.ceil(Ee/1e3):Math.ceil(e/1e3)}async function X(t,e){let{query:r,limit:s=10,categories:n,timeRange:o,apiKey:i}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!i)throw new Error("Tavily API key is required");if(e?.aborted)throw e.reason instanceof Error?e.reason:new Error("Tavily search aborted");let l=Re({apiKey:i}),u={maxResults:s,timeout:Ae(t.timeout)},a=Ie(n);a&&(u.topic=a);let p=Oe(o);p&&(u.timeRange=p);try{return{results:(await l.search(r,u)).results.map(m=>({title:m.title,url:m.url,snippet:m.content,engine:"tavily"})),success:!0}}catch(g){let h=g instanceof Error?g.message:"Tavily search error.";throw d.error(h),g}}var v=new x("[LocalSearch]");function ee(t){return G.includes(t)}function Pe(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function re(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let i=o.filter(ee),l=o.filter(a=>!ee(a));if(l.length>0&&v.warn(`Invalid search engines ignored: ${l.join(", ")}`),i.length===0)throw new Error(`No valid search engines provided. Valid engines: ${G.join(", ")}`);let u=new b({headless:!0,timeout:3e4});return await E({browser:u,signal:e,task:async()=>{let a=[];for(let p of i)try{v.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>Pe(m,p));a.push(...h),v.info(`Found ${g.length} results from ${p}`);break}}catch(g){v.error(`Failed to search with ${p}:`,g)}return v.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw v.error(p,a),a})}var Ce="https://www.googleapis.com/customsearch/v1",_e=2e4;async function te(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:i}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});i&&i!=="auto"&&l.set("lr",`lang_${i}`);let u=f({signal:e,timeoutMs:_e,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${Ce}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var ke="https://open.bigmodel.cn/api/paas/v4/web_search",Le=2e4;async function se(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=f({signal:e,timeoutMs:Le,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(ke,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var Me="https://api.exa.ai/search",Ue=2e4,$e=1e4;async function ne(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=f({signal:e,timeoutMs:Ue,timeoutMessage:"Exa search timeout"});try{let i=await fetch(Me,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:$e}}}),signal:o.signal});if(!i.ok)throw new Error(`Exa search failed: ${i.status} ${i.statusText}`);return{results:(await i.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(i){let l=i instanceof Error?i.message:"Exa search error.";throw d.error(l),i}finally{o.cleanup()}}var De="https://api.bocha.cn/v1/web-search",qe=2e4;async function oe(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let i="noLimit";o&&(i={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=f({signal:e,timeoutMs:qe,timeoutMessage:"Bocha search timeout"});try{let u=await fetch(De,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:i}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var Be={searxngSearch:async(t,e)=>await J(t,e),tavilySearch:async(t,e)=>await X(t,e),bingSearch:async(t,e)=>await Y(t,e),duckDuckGoSearch:async(t,e)=>await Q(t,e),localSearch:async(t,e)=>await re(t,e),googleSearch:async(t,e)=>await te(t,e),zhipuSearch:async(t,e)=>await se(t,e),exaSearch:async(t,e)=>await ne(t,e),bochaSearch:async(t,e)=>await oe(t,e)};async function ae(t,e,r,s=Be){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey},{categories:o,language:i}=e.defaultSearchOptions;return o&&(n.categories=o),i&&(n.language=i),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[F.STRICT,F.MODERATE,F.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}async function ie(t,e){let r=[];for(let s of t)try{let n=await e.scrapeUrl(s,{formats:["markdown"],onlyMainContent:!0});if(n.success&&n.markdown){r.push(`## Content from ${s}
|
|
8
|
+
Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(D.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(D.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(i=>{try{return new URL(i).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(D.SEARCH_WAIT_MS);let i=await this.getHtml();return this.extractSearchResults(s,i).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return U.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=L.load(e),s=[];return r("div.g").each((n,o)=>{let i=r(o),l=i.find("a[href]").first(),u=i.find("h3").first(),a=i.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=L.load(e),s=[];return r("li.b_algo").each((n,o)=>{let i=r(o),l=i.find("h2 a").first(),u=i.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=L.load(e),s=[];return r("div.result").each((n,o)=>{let i=r(o),l=i.find("h3 a").first(),u=i.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=L.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let i=r(o),l=i.find("h3 a").first(),u=i.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let i=new URL(o[1],r).href;n.includes(i)||n.push(i)}catch{}return n}};function fe(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var k=fe();function Se(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function V(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function v({browser:t,signal:e,task:r}){let s=k.track(t),n=Se(t,s);if(e?.aborted)throw await n(),V(e);let o;try{let i=r();if(!e)return await i;let l=new Promise((u,a)=>{o=()=>{n(),a(V(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([i,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}import{SafeSearchType as F}from"duck-duck-scrape";var d=new R("search");function S({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var we=2e4;async function Z(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:i="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=S({signal:e,timeoutMs:we,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([f,E])=>{E!==void 0&&h.set(f,E.toString())});let m=await fetch(`${i}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(f=>({title:f.name,snippet:f.snippet,url:f.url,source:f.siteName,thumbnailUrl:f.thumbnailUrl,language:f.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}import*as q from"duck-duck-scrape";import ye from"async-retry";async function Y(t,e){let{query:r,timeout:s=1e4,safeSearch:n=q.SafeSearchType.OFF,retry:o={retries:3},...i}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await ye(async a=>{try{return await q.search(r,{...i,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}var be=new Set(["day","month","year"]);async function Q(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:i="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let B=S({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let x=new URLSearchParams({q:r,pageno:String(s),categories:o,format:u,safesearch:String(l),language:a,engines:i});be.has(p)&&x.set("time_range",p);let I={};h&&(I.Authorization=`Bearer ${h}`);let f=await fetch(`${m}/search?${x.toString()}`,{method:"GET",headers:I,signal:B.signal});if(!f.ok)throw new Error(`SearxNG search failed: ${f.status} ${f.statusText}`);let E;try{E=await f.json()}catch{throw new Error("SearxNG search returned invalid JSON response")}return E.results?{results:E.results.slice(0,n).map(w=>{let le=w.img_src?{thumbnail:w.thumbnail_src,src:w.img_src}:null,ue=w.iframe_src?{thumbnail:w.thumbnail_src,src:w.iframe_src}:null;return{title:w.title,snippet:w.content,url:w.url,source:w.source,image:le,video:ue,engine:w.engine}}),success:!0}:{results:[],success:!1}}catch(x){let I=x instanceof Error?x.message:"Searxng search error.";throw d.error(I),x}finally{B.cleanup()}}import{tavily as xe}from"@tavily/core";var Re=2e4,Ee=new Set(["general","news","finance"]),ve=new Set(["day","week","month","year","d","w","m","y"]);function Te(t){if(!(!t||!Ee.has(t)))return t}function Ie(t){if(!(!t||!ve.has(t)))return t}function Ae(t){let e=Number(t);return!Number.isFinite(e)||e<=0?Math.ceil(Re/1e3):Math.ceil(e/1e3)}async function J(t,e){let{query:r,limit:s=10,categories:n,timeRange:o,apiKey:i}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!i)throw new Error("Tavily API key is required");if(e?.aborted)throw e.reason instanceof Error?e.reason:new Error("Tavily search aborted");let l=xe({apiKey:i}),u={maxResults:s,timeout:Ae(t.timeout)},a=Te(n);a&&(u.topic=a);let p=Ie(o);p&&(u.timeRange=p);try{return{results:(await l.search(r,u)).results.map(m=>({title:m.title,url:m.url,snippet:m.content,engine:"tavily"})),success:!0}}catch(g){let h=g instanceof Error?g.message:"Tavily search error.";throw d.error(h),g}}var T=new R("[LocalSearch]");function X(t){return G.includes(t)}function Oe(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function ee(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let i=o.filter(X),l=o.filter(a=>!X(a));if(l.length>0&&T.warn(`Invalid search engines ignored: ${l.join(", ")}`),i.length===0)throw new Error(`No valid search engines provided. Valid engines: ${G.join(", ")}`);let u=new b({headless:!0,timeout:3e4});return await v({browser:u,signal:e,task:async()=>{let a=[];for(let p of i)try{T.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>Oe(m,p));a.push(...h),T.info(`Found ${g.length} results from ${p}`);break}}catch(g){T.error(`Failed to search with ${p}:`,g)}return T.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw T.error(p,a),a})}var Pe="https://www.googleapis.com/customsearch/v1",Ce=2e4;async function re(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:i}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});i&&i!=="auto"&&l.set("lr",`lang_${i}`);let u=S({signal:e,timeoutMs:Ce,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${Pe}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var _e="https://open.bigmodel.cn/api/paas/v4/web_search",Le=2e4;async function te(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=S({signal:e,timeoutMs:Le,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(_e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var ke="https://api.exa.ai/search",Me=2e4,Ue=1e4;async function se(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=S({signal:e,timeoutMs:Me,timeoutMessage:"Exa search timeout"});try{let i=await fetch(ke,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:Ue}}}),signal:o.signal});if(!i.ok)throw new Error(`Exa search failed: ${i.status} ${i.statusText}`);return{results:(await i.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(i){let l=i instanceof Error?i.message:"Exa search error.";throw d.error(l),i}finally{o.cleanup()}}var $e="https://api.bocha.cn/v1/web-search",De=2e4;async function ne(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let i="noLimit";o&&(i={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=S({signal:e,timeoutMs:De,timeoutMessage:"Bocha search timeout"});try{let u=await fetch($e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:i}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var qe={searxngSearch:async(t,e)=>await Q(t,e),tavilySearch:async(t,e)=>await J(t,e),bingSearch:async(t,e)=>await Z(t,e),duckDuckGoSearch:async(t,e)=>await Y(t,e),localSearch:async(t,e)=>await ee(t,e),googleSearch:async(t,e)=>await re(t,e),zhipuSearch:async(t,e)=>await te(t,e),exaSearch:async(t,e)=>await se(t,e),bochaSearch:async(t,e)=>await ne(t,e)};async function oe(t,e,r,s=qe){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},{categories:o,language:i}=e.defaultSearchOptions;return o&&(n.categories=o),i&&(n.language=i),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[F.STRICT,F.MODERATE,F.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}async function ae(t,e){let r=[];for(let s of t)try{let n=await e.scrapeUrl(s,{formats:["markdown"],onlyMainContent:!0});if(n.success&&n.markdown){r.push(`## Content from ${s}
|
|
9
9
|
|
|
10
10
|
${n.markdown}`);continue}r.push(`## Failed to extract from ${s}
|
|
11
11
|
|
|
@@ -15,15 +15,15 @@ Error: ${o}`)}return r.join(`
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
`)}function
|
|
18
|
+
`)}function M(t,e,r={}){return async(s,n)=>{let o=Date.now();try{r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request started for tool: [${t}]`});let i=await e(s,n);return r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request completed in ${Date.now()-o}ms`}),i}catch(i){return r.logMessage&&await r.logMessage({level:"error",data:`[${new Date().toISOString()}] Error in ${t}: ${i}`}),{content:[{type:"text",text:i instanceof Error?i.message:String(i)}],isError:!0}}}}function ie(t,e){let r,s=async()=>{r||(r=e()),await r},n=o=>{t.once(o,()=>{s().finally(()=>{typeof t.kill=="function"&&typeof t.pid=="number"&&t.kill(t.pid,o)})})};t.once("beforeExit",()=>{s()}),n("SIGINT"),n("SIGTERM")}function ce(t,e,r){let s,n=!1,o=u=>{n||(n=!0,r(u))},i=async()=>{s||(s=e()),await s},l=()=>{i().then(()=>{o(0)}).catch(()=>{o(1)})};t.once("end",l),t.once("close",l)}import Ge from"@dotenvx/dotenvx";Ge.config({quiet:!0});var Fe=process.env.SEARCH_API_URL,Ne=process.env.SEARCH_API_KEY,je=process.env.SEARCH_PROVIDER??"local",Ke=process.env.SAFE_SEARCH??0,ze=process.env.LIMIT??10,We=process.env.CATEGORIES??"general",Ve=process.env.ENGINES??"all",Ze=process.env.FORMAT??"json",Ye=process.env.LANGUAGE??"auto",Qe=process.env.TIME_RANGE??"",Je=process.env.TIMEOUT??1e4,y=new Be({name:"one-search-mcp",version:"1.2.0"},{capabilities:{tools:{},logging:{}}}),Xe={limit:Number(ze),categories:We,format:Ze,safeSearch:Number(Ke),language:Ye,engines:Ve,timeRange:Qe,timeout:Je};ie(process,async()=>{await k.cleanup()});ce(process.stdin,async()=>{await k.cleanup(),await y.close()},t=>{process.exit(t)});y.registerTool(A.name,{description:A.description,inputSchema:A.schema},M(A.name,async(t,e)=>{let{results:r,success:s}=await oe(t,{provider:je,apiKey:Ne??"",apiUrl:Fe,defaultSearchOptions:Xe},e.signal);if(!s)throw new Error("Failed to search");return{content:[{type:"text",text:r.map(o=>`Title: ${o.title}
|
|
19
19
|
URL: ${o.url}
|
|
20
20
|
Description: ${o.snippet}
|
|
21
21
|
${o.markdown?`Content: ${o.markdown}`:""}`).join(`
|
|
22
22
|
|
|
23
|
-
`)}]}},{logMessage:t=>
|
|
23
|
+
`)}]}},{logMessage:t=>y.sendLoggingMessage(t)}));y.registerTool(P.name,{description:P.description,inputSchema:P.schema},M(P.name,async(t,e)=>{let{url:r,...s}=t,{content:n}=await er(r,s,e.signal);return{content:n}},{logMessage:t=>y.sendLoggingMessage(t)}));y.registerTool(O.name,{description:O.description,inputSchema:O.schema},M(O.name,async(t,e)=>{let{content:r}=await rr(t.url,t,e.signal);return{content:r}},{logMessage:t=>y.sendLoggingMessage(t)}));y.registerTool(C.name,{description:C.description,inputSchema:C.schema},M(C.name,async(t,e)=>{let{content:r}=await tr(t,e.signal);return{content:r}},{logMessage:t=>y.sendLoggingMessage(t)}));async function er(t,e,r){let s=new b({headless:!0,timeout:3e4});return await v({browser:s,signal:r,task:async()=>{let n=await s.scrapeUrl(t,e);if(!n.success)throw new Error(`Failed to scrape: ${n.error}`);let o=[];return n.markdown&&o.push(n.markdown),n.rawHtml&&o.push(n.rawHtml),n.links&&o.push(n.links.join(`
|
|
24
24
|
`)),n.screenshot&&o.push(n.screenshot),n.html&&o.push(n.html),{content:[{type:"text",text:o.join(`
|
|
25
25
|
|
|
26
|
-
`)||"No content found"}],result:n,success:!0}}})}async function
|
|
27
|
-
`).trim()}],result:n.links,success:!0}}})}async function
|
|
28
|
-
`),process.exit(1)}}
|
|
26
|
+
`)||"No content found"}],result:n,success:!0}}})}async function rr(t,e,r){let s=new b({headless:!0,timeout:3e4});return await v({browser:s,signal:r,task:async()=>{let n=await s.mapUrl(t,e);if(!n.success)throw new Error(`Failed to map: ${n.error}`);if(!n.links)throw new Error(`No links found from: ${t}`);return{content:[{type:"text",text:n.links.join(`
|
|
27
|
+
`).trim()}],result:n.links,success:!0}}})}async function tr(t,e){let{urls:r}=t,s=new b({headless:!0,timeout:3e4});return await v({browser:s,signal:e,task:async()=>({content:[{type:"text",text:await ae(r,s)}]})})}async function sr(){try{let t=new He;await y.connect(t),await y.sendLoggingMessage({level:"info",data:"OneSearch MCP server started"})}catch(t){let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error starting server: ${e}
|
|
28
|
+
`),process.exit(1)}}sr().catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error running server: ${e}
|
|
29
29
|
`),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "one-search-mcp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "One Search MCP Server, Web Search & Crawl & Scraper & Extract, support agent-browser, SearXNG, Tavily, DuckDuckGo, Bing, etc.",
|
|
5
5
|
"mcpName": "io.github.yokingma/one-search-mcp",
|
|
6
6
|
"private": false,
|