@iflow-mcp/jakeliume-webpeel 0.22.0

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.
Files changed (547) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +313 -0
  3. package/dist/cache.d.ts +30 -0
  4. package/dist/cache.js +139 -0
  5. package/dist/cli/commands/auth.d.ts +5 -0
  6. package/dist/cli/commands/auth.js +411 -0
  7. package/dist/cli/commands/doctor.d.ts +37 -0
  8. package/dist/cli/commands/doctor.js +371 -0
  9. package/dist/cli/commands/fetch.d.ts +6 -0
  10. package/dist/cli/commands/fetch.js +1345 -0
  11. package/dist/cli/commands/guide.d.ts +2 -0
  12. package/dist/cli/commands/guide.js +183 -0
  13. package/dist/cli/commands/interact.d.ts +5 -0
  14. package/dist/cli/commands/interact.js +840 -0
  15. package/dist/cli/commands/jobs.d.ts +5 -0
  16. package/dist/cli/commands/jobs.js +997 -0
  17. package/dist/cli/commands/monitor.d.ts +12 -0
  18. package/dist/cli/commands/monitor.js +197 -0
  19. package/dist/cli/commands/observe.d.ts +12 -0
  20. package/dist/cli/commands/observe.js +158 -0
  21. package/dist/cli/commands/screenshot.d.ts +5 -0
  22. package/dist/cli/commands/screenshot.js +282 -0
  23. package/dist/cli/commands/search.d.ts +5 -0
  24. package/dist/cli/commands/search.js +1021 -0
  25. package/dist/cli/commands/setup.d.ts +13 -0
  26. package/dist/cli/commands/setup.js +244 -0
  27. package/dist/cli/commands/skill.d.ts +15 -0
  28. package/dist/cli/commands/skill.js +195 -0
  29. package/dist/cli/utils.d.ts +84 -0
  30. package/dist/cli/utils.js +806 -0
  31. package/dist/cli-auth.d.ts +75 -0
  32. package/dist/cli-auth.js +369 -0
  33. package/dist/cli.d.ts +17 -0
  34. package/dist/cli.js +99 -0
  35. package/dist/core/actions.d.ts +69 -0
  36. package/dist/core/actions.js +495 -0
  37. package/dist/core/agent.d.ts +98 -0
  38. package/dist/core/agent.js +558 -0
  39. package/dist/core/answer.d.ts +42 -0
  40. package/dist/core/answer.js +395 -0
  41. package/dist/core/application-tracker.d.ts +84 -0
  42. package/dist/core/application-tracker.js +184 -0
  43. package/dist/core/apply.d.ts +162 -0
  44. package/dist/core/apply.js +816 -0
  45. package/dist/core/auth-detection.d.ts +35 -0
  46. package/dist/core/auth-detection.js +358 -0
  47. package/dist/core/auto-extract.d.ts +82 -0
  48. package/dist/core/auto-extract.js +604 -0
  49. package/dist/core/auto-interact.d.ts +23 -0
  50. package/dist/core/auto-interact.js +246 -0
  51. package/dist/core/bm25-filter.d.ts +66 -0
  52. package/dist/core/bm25-filter.js +288 -0
  53. package/dist/core/branding.d.ts +54 -0
  54. package/dist/core/branding.js +234 -0
  55. package/dist/core/browser-fetch.d.ts +323 -0
  56. package/dist/core/browser-fetch.js +1600 -0
  57. package/dist/core/browser-pool.d.ts +91 -0
  58. package/dist/core/browser-pool.js +550 -0
  59. package/dist/core/budget.d.ts +42 -0
  60. package/dist/core/budget.js +324 -0
  61. package/dist/core/business-intel.d.ts +47 -0
  62. package/dist/core/business-intel.js +279 -0
  63. package/dist/core/cache.d.ts +13 -0
  64. package/dist/core/cache.js +121 -0
  65. package/dist/core/cf-worker-proxy.d.ts +32 -0
  66. package/dist/core/cf-worker-proxy.js +87 -0
  67. package/dist/core/challenge-detection.d.ts +26 -0
  68. package/dist/core/challenge-detection.js +468 -0
  69. package/dist/core/change-tracking.d.ts +75 -0
  70. package/dist/core/change-tracking.js +276 -0
  71. package/dist/core/chunker.d.ts +46 -0
  72. package/dist/core/chunker.js +249 -0
  73. package/dist/core/chunking.d.ts +42 -0
  74. package/dist/core/chunking.js +181 -0
  75. package/dist/core/circuit-breaker.d.ts +44 -0
  76. package/dist/core/circuit-breaker.js +85 -0
  77. package/dist/core/content-pruner.d.ts +47 -0
  78. package/dist/core/content-pruner.js +425 -0
  79. package/dist/core/cookie-cache.d.ts +60 -0
  80. package/dist/core/cookie-cache.js +163 -0
  81. package/dist/core/crawl-checkpoint.d.ts +54 -0
  82. package/dist/core/crawl-checkpoint.js +104 -0
  83. package/dist/core/crawler.d.ts +84 -0
  84. package/dist/core/crawler.js +349 -0
  85. package/dist/core/cross-verify.d.ts +27 -0
  86. package/dist/core/cross-verify.js +93 -0
  87. package/dist/core/deep-fetch.d.ts +74 -0
  88. package/dist/core/deep-fetch.js +405 -0
  89. package/dist/core/deep-research.d.ts +141 -0
  90. package/dist/core/deep-research.js +972 -0
  91. package/dist/core/design-analysis.d.ts +70 -0
  92. package/dist/core/design-analysis.js +490 -0
  93. package/dist/core/design-compare.d.ts +38 -0
  94. package/dist/core/design-compare.js +264 -0
  95. package/dist/core/diff.d.ts +61 -0
  96. package/dist/core/diff.js +289 -0
  97. package/dist/core/dns-cache.d.ts +20 -0
  98. package/dist/core/dns-cache.js +198 -0
  99. package/dist/core/documents.d.ts +23 -0
  100. package/dist/core/documents.js +123 -0
  101. package/dist/core/domain-memory.d.ts +66 -0
  102. package/dist/core/domain-memory.js +163 -0
  103. package/dist/core/domain-verify.d.ts +40 -0
  104. package/dist/core/domain-verify.js +379 -0
  105. package/dist/core/engine-ranker.d.ts +112 -0
  106. package/dist/core/engine-ranker.js +395 -0
  107. package/dist/core/extract-inline.d.ts +38 -0
  108. package/dist/core/extract-inline.js +215 -0
  109. package/dist/core/extract-listings.d.ts +38 -0
  110. package/dist/core/extract-listings.js +461 -0
  111. package/dist/core/extract.d.ts +9 -0
  112. package/dist/core/extract.js +139 -0
  113. package/dist/core/fetch-cache.d.ts +57 -0
  114. package/dist/core/fetch-cache.js +95 -0
  115. package/dist/core/fetcher.d.ts +13 -0
  116. package/dist/core/fetcher.js +12 -0
  117. package/dist/core/google-cache.d.ts +29 -0
  118. package/dist/core/google-cache.js +180 -0
  119. package/dist/core/google-serp-parser.d.ts +82 -0
  120. package/dist/core/google-serp-parser.js +287 -0
  121. package/dist/core/hotel-search.d.ts +122 -0
  122. package/dist/core/hotel-search.js +382 -0
  123. package/dist/core/http-fetch.d.ts +72 -0
  124. package/dist/core/http-fetch.js +820 -0
  125. package/dist/core/human.d.ts +175 -0
  126. package/dist/core/human.js +680 -0
  127. package/dist/core/image-caption.d.ts +44 -0
  128. package/dist/core/image-caption.js +271 -0
  129. package/dist/core/jobs.d.ts +75 -0
  130. package/dist/core/jobs.js +634 -0
  131. package/dist/core/json-ld.d.ts +15 -0
  132. package/dist/core/json-ld.js +617 -0
  133. package/dist/core/language-detect.d.ts +18 -0
  134. package/dist/core/language-detect.js +135 -0
  135. package/dist/core/links.d.ts +10 -0
  136. package/dist/core/links.js +44 -0
  137. package/dist/core/llm-extract.d.ts +71 -0
  138. package/dist/core/llm-extract.js +507 -0
  139. package/dist/core/llm-provider.d.ts +100 -0
  140. package/dist/core/llm-provider.js +702 -0
  141. package/dist/core/local-search.d.ts +60 -0
  142. package/dist/core/local-search.js +308 -0
  143. package/dist/core/logger.d.ts +28 -0
  144. package/dist/core/logger.js +104 -0
  145. package/dist/core/map.d.ts +33 -0
  146. package/dist/core/map.js +127 -0
  147. package/dist/core/markdown.d.ts +92 -0
  148. package/dist/core/markdown.js +809 -0
  149. package/dist/core/metadata.d.ts +34 -0
  150. package/dist/core/metadata.js +422 -0
  151. package/dist/core/observe.d.ts +113 -0
  152. package/dist/core/observe.js +395 -0
  153. package/dist/core/ocr.d.ts +12 -0
  154. package/dist/core/ocr.js +33 -0
  155. package/dist/core/paginate.d.ts +31 -0
  156. package/dist/core/paginate.js +106 -0
  157. package/dist/core/pdf.d.ts +8 -0
  158. package/dist/core/pdf.js +25 -0
  159. package/dist/core/peel-tls.d.ts +25 -0
  160. package/dist/core/peel-tls.js +220 -0
  161. package/dist/core/pipeline.d.ts +132 -0
  162. package/dist/core/pipeline.js +1666 -0
  163. package/dist/core/profiles.d.ts +61 -0
  164. package/dist/core/profiles.js +350 -0
  165. package/dist/core/prompt-guard.d.ts +30 -0
  166. package/dist/core/prompt-guard.js +119 -0
  167. package/dist/core/proxy-config.d.ts +90 -0
  168. package/dist/core/proxy-config.js +172 -0
  169. package/dist/core/quick-answer.d.ts +53 -0
  170. package/dist/core/quick-answer.js +833 -0
  171. package/dist/core/rate-governor.d.ts +80 -0
  172. package/dist/core/rate-governor.js +238 -0
  173. package/dist/core/readability.d.ts +57 -0
  174. package/dist/core/readability.js +533 -0
  175. package/dist/core/research.d.ts +66 -0
  176. package/dist/core/research.js +270 -0
  177. package/dist/core/retry.d.ts +60 -0
  178. package/dist/core/retry.js +119 -0
  179. package/dist/core/safe-browsing.d.ts +30 -0
  180. package/dist/core/safe-browsing.js +206 -0
  181. package/dist/core/schema-extraction.d.ts +66 -0
  182. package/dist/core/schema-extraction.js +352 -0
  183. package/dist/core/schema-postprocess.d.ts +32 -0
  184. package/dist/core/schema-postprocess.js +469 -0
  185. package/dist/core/schema-templates.d.ts +19 -0
  186. package/dist/core/schema-templates.js +143 -0
  187. package/dist/core/screenshot.d.ts +224 -0
  188. package/dist/core/screenshot.js +207 -0
  189. package/dist/core/search-engines.d.ts +25 -0
  190. package/dist/core/search-engines.js +182 -0
  191. package/dist/core/search-provider.d.ts +243 -0
  192. package/dist/core/search-provider.js +1629 -0
  193. package/dist/core/searxng-provider.d.ts +35 -0
  194. package/dist/core/searxng-provider.js +105 -0
  195. package/dist/core/selective-evidence.d.ts +151 -0
  196. package/dist/core/selective-evidence.js +389 -0
  197. package/dist/core/site-search.d.ts +44 -0
  198. package/dist/core/site-search.js +252 -0
  199. package/dist/core/sitemap.d.ts +23 -0
  200. package/dist/core/sitemap.js +105 -0
  201. package/dist/core/source-credibility.d.ts +29 -0
  202. package/dist/core/source-credibility.js +584 -0
  203. package/dist/core/source-scoring.d.ts +166 -0
  204. package/dist/core/source-scoring.js +396 -0
  205. package/dist/core/stemmer.d.ts +38 -0
  206. package/dist/core/stemmer.js +509 -0
  207. package/dist/core/strategies.d.ts +104 -0
  208. package/dist/core/strategies.js +1044 -0
  209. package/dist/core/strategy-hooks.d.ts +145 -0
  210. package/dist/core/strategy-hooks.js +74 -0
  211. package/dist/core/structured-extract.d.ts +43 -0
  212. package/dist/core/structured-extract.js +550 -0
  213. package/dist/core/summarize.d.ts +17 -0
  214. package/dist/core/summarize.js +78 -0
  215. package/dist/core/synonyms.d.ts +42 -0
  216. package/dist/core/synonyms.js +184 -0
  217. package/dist/core/system-monitor.d.ts +61 -0
  218. package/dist/core/system-monitor.js +133 -0
  219. package/dist/core/table-format.d.ts +30 -0
  220. package/dist/core/table-format.js +146 -0
  221. package/dist/core/threat-feeds.d.ts +23 -0
  222. package/dist/core/threat-feeds.js +104 -0
  223. package/dist/core/timing.d.ts +21 -0
  224. package/dist/core/timing.js +33 -0
  225. package/dist/core/transcript-export.d.ts +47 -0
  226. package/dist/core/transcript-export.js +107 -0
  227. package/dist/core/user-agents.d.ts +82 -0
  228. package/dist/core/user-agents.js +239 -0
  229. package/dist/core/vertical-search.d.ts +54 -0
  230. package/dist/core/vertical-search.js +158 -0
  231. package/dist/core/watch-manager.d.ts +175 -0
  232. package/dist/core/watch-manager.js +416 -0
  233. package/dist/core/watch.d.ts +101 -0
  234. package/dist/core/watch.js +389 -0
  235. package/dist/core/youtube.d.ts +130 -0
  236. package/dist/core/youtube.js +1175 -0
  237. package/dist/ee/challenge-re-export.d.ts +1 -0
  238. package/dist/ee/challenge-re-export.js +1 -0
  239. package/dist/ee/challenge-solver.d.ts +72 -0
  240. package/dist/ee/challenge-solver.js +720 -0
  241. package/dist/ee/domain-extractors.d.ts +8 -0
  242. package/dist/ee/domain-extractors.js +8 -0
  243. package/dist/ee/domain-intel.d.ts +16 -0
  244. package/dist/ee/domain-intel.js +133 -0
  245. package/dist/ee/extractors/allrecipes.d.ts +2 -0
  246. package/dist/ee/extractors/allrecipes.js +120 -0
  247. package/dist/ee/extractors/amazon.d.ts +2 -0
  248. package/dist/ee/extractors/amazon.js +78 -0
  249. package/dist/ee/extractors/arxiv.d.ts +2 -0
  250. package/dist/ee/extractors/arxiv.js +137 -0
  251. package/dist/ee/extractors/bestbuy.d.ts +2 -0
  252. package/dist/ee/extractors/bestbuy.js +78 -0
  253. package/dist/ee/extractors/carscom.d.ts +2 -0
  254. package/dist/ee/extractors/carscom.js +121 -0
  255. package/dist/ee/extractors/coingecko.d.ts +2 -0
  256. package/dist/ee/extractors/coingecko.js +134 -0
  257. package/dist/ee/extractors/craigslist.d.ts +2 -0
  258. package/dist/ee/extractors/craigslist.js +92 -0
  259. package/dist/ee/extractors/devto.d.ts +2 -0
  260. package/dist/ee/extractors/devto.js +135 -0
  261. package/dist/ee/extractors/ebay.d.ts +2 -0
  262. package/dist/ee/extractors/ebay.js +90 -0
  263. package/dist/ee/extractors/espn.d.ts +2 -0
  264. package/dist/ee/extractors/espn.js +260 -0
  265. package/dist/ee/extractors/etsy.d.ts +2 -0
  266. package/dist/ee/extractors/etsy.js +52 -0
  267. package/dist/ee/extractors/facebook.d.ts +2 -0
  268. package/dist/ee/extractors/facebook.js +46 -0
  269. package/dist/ee/extractors/github.d.ts +2 -0
  270. package/dist/ee/extractors/github.js +196 -0
  271. package/dist/ee/extractors/google-flights.d.ts +2 -0
  272. package/dist/ee/extractors/google-flights.js +176 -0
  273. package/dist/ee/extractors/hackernews.d.ts +2 -0
  274. package/dist/ee/extractors/hackernews.js +147 -0
  275. package/dist/ee/extractors/imdb.d.ts +2 -0
  276. package/dist/ee/extractors/imdb.js +172 -0
  277. package/dist/ee/extractors/index.d.ts +26 -0
  278. package/dist/ee/extractors/index.js +247 -0
  279. package/dist/ee/extractors/instagram.d.ts +2 -0
  280. package/dist/ee/extractors/instagram.js +102 -0
  281. package/dist/ee/extractors/kalshi.d.ts +2 -0
  282. package/dist/ee/extractors/kalshi.js +121 -0
  283. package/dist/ee/extractors/kayak-cars.d.ts +2 -0
  284. package/dist/ee/extractors/kayak-cars.js +270 -0
  285. package/dist/ee/extractors/linkedin.d.ts +2 -0
  286. package/dist/ee/extractors/linkedin.js +113 -0
  287. package/dist/ee/extractors/medium.d.ts +2 -0
  288. package/dist/ee/extractors/medium.js +130 -0
  289. package/dist/ee/extractors/news.d.ts +4 -0
  290. package/dist/ee/extractors/news.js +173 -0
  291. package/dist/ee/extractors/npm.d.ts +2 -0
  292. package/dist/ee/extractors/npm.js +86 -0
  293. package/dist/ee/extractors/pdf.d.ts +2 -0
  294. package/dist/ee/extractors/pdf.js +108 -0
  295. package/dist/ee/extractors/pinterest.d.ts +2 -0
  296. package/dist/ee/extractors/pinterest.js +34 -0
  297. package/dist/ee/extractors/polymarket.d.ts +2 -0
  298. package/dist/ee/extractors/polymarket.js +358 -0
  299. package/dist/ee/extractors/producthunt.d.ts +2 -0
  300. package/dist/ee/extractors/producthunt.js +88 -0
  301. package/dist/ee/extractors/pubmed.d.ts +2 -0
  302. package/dist/ee/extractors/pubmed.js +162 -0
  303. package/dist/ee/extractors/pypi.d.ts +2 -0
  304. package/dist/ee/extractors/pypi.js +80 -0
  305. package/dist/ee/extractors/reddit.d.ts +2 -0
  306. package/dist/ee/extractors/reddit.js +438 -0
  307. package/dist/ee/extractors/redfin.d.ts +2 -0
  308. package/dist/ee/extractors/redfin.js +156 -0
  309. package/dist/ee/extractors/semanticscholar.d.ts +2 -0
  310. package/dist/ee/extractors/semanticscholar.js +131 -0
  311. package/dist/ee/extractors/shared.d.ts +12 -0
  312. package/dist/ee/extractors/shared.js +76 -0
  313. package/dist/ee/extractors/soundcloud.d.ts +2 -0
  314. package/dist/ee/extractors/soundcloud.js +34 -0
  315. package/dist/ee/extractors/sportsbetting.d.ts +2 -0
  316. package/dist/ee/extractors/sportsbetting.js +37 -0
  317. package/dist/ee/extractors/spotify.d.ts +2 -0
  318. package/dist/ee/extractors/spotify.js +34 -0
  319. package/dist/ee/extractors/stackoverflow.d.ts +2 -0
  320. package/dist/ee/extractors/stackoverflow.js +61 -0
  321. package/dist/ee/extractors/substack.d.ts +2 -0
  322. package/dist/ee/extractors/substack.js +115 -0
  323. package/dist/ee/extractors/substackroot.d.ts +2 -0
  324. package/dist/ee/extractors/substackroot.js +46 -0
  325. package/dist/ee/extractors/tiktok.d.ts +2 -0
  326. package/dist/ee/extractors/tiktok.js +29 -0
  327. package/dist/ee/extractors/tradingview.d.ts +2 -0
  328. package/dist/ee/extractors/tradingview.js +182 -0
  329. package/dist/ee/extractors/twitch.d.ts +2 -0
  330. package/dist/ee/extractors/twitch.js +36 -0
  331. package/dist/ee/extractors/twitter.d.ts +2 -0
  332. package/dist/ee/extractors/twitter.js +327 -0
  333. package/dist/ee/extractors/types.d.ts +14 -0
  334. package/dist/ee/extractors/types.js +1 -0
  335. package/dist/ee/extractors/walmart.d.ts +2 -0
  336. package/dist/ee/extractors/walmart.js +50 -0
  337. package/dist/ee/extractors/weather.d.ts +2 -0
  338. package/dist/ee/extractors/weather.js +133 -0
  339. package/dist/ee/extractors/wikipedia.d.ts +4 -0
  340. package/dist/ee/extractors/wikipedia.js +235 -0
  341. package/dist/ee/extractors/yelp.d.ts +2 -0
  342. package/dist/ee/extractors/yelp.js +216 -0
  343. package/dist/ee/extractors/youtube.d.ts +2 -0
  344. package/dist/ee/extractors/youtube.js +189 -0
  345. package/dist/ee/extractors/zillow.d.ts +54 -0
  346. package/dist/ee/extractors/zillow.js +247 -0
  347. package/dist/ee/extractors-re-export.d.ts +1 -0
  348. package/dist/ee/extractors-re-export.js +1 -0
  349. package/dist/ee/premium-hooks.d.ts +20 -0
  350. package/dist/ee/premium-hooks.js +50 -0
  351. package/dist/ee/spa-detection.d.ts +2 -0
  352. package/dist/ee/spa-detection.js +2 -0
  353. package/dist/ee/stability.d.ts +4 -0
  354. package/dist/ee/stability.js +29 -0
  355. package/dist/ee/swr-cache.d.ts +14 -0
  356. package/dist/ee/swr-cache.js +34 -0
  357. package/dist/index.d.ts +143 -0
  358. package/dist/index.js +291 -0
  359. package/dist/integrations/index.d.ts +2 -0
  360. package/dist/integrations/index.js +2 -0
  361. package/dist/integrations/langchain.d.ts +64 -0
  362. package/dist/integrations/langchain.js +115 -0
  363. package/dist/integrations/llamaindex.d.ts +50 -0
  364. package/dist/integrations/llamaindex.js +91 -0
  365. package/dist/mcp/handlers/act.d.ts +5 -0
  366. package/dist/mcp/handlers/act.js +34 -0
  367. package/dist/mcp/handlers/definitions.d.ts +6 -0
  368. package/dist/mcp/handlers/definitions.js +395 -0
  369. package/dist/mcp/handlers/extract.d.ts +7 -0
  370. package/dist/mcp/handlers/extract.js +135 -0
  371. package/dist/mcp/handlers/fetch.d.ts +6 -0
  372. package/dist/mcp/handlers/fetch.js +98 -0
  373. package/dist/mcp/handlers/find.d.ts +5 -0
  374. package/dist/mcp/handlers/find.js +137 -0
  375. package/dist/mcp/handlers/index.d.ts +13 -0
  376. package/dist/mcp/handlers/index.js +63 -0
  377. package/dist/mcp/handlers/legacy.d.ts +25 -0
  378. package/dist/mcp/handlers/legacy.js +450 -0
  379. package/dist/mcp/handlers/meta.d.ts +6 -0
  380. package/dist/mcp/handlers/meta.js +40 -0
  381. package/dist/mcp/handlers/monitor.d.ts +5 -0
  382. package/dist/mcp/handlers/monitor.js +41 -0
  383. package/dist/mcp/handlers/observe.d.ts +8 -0
  384. package/dist/mcp/handlers/observe.js +37 -0
  385. package/dist/mcp/handlers/read.d.ts +6 -0
  386. package/dist/mcp/handlers/read.js +78 -0
  387. package/dist/mcp/handlers/see.d.ts +5 -0
  388. package/dist/mcp/handlers/see.js +75 -0
  389. package/dist/mcp/handlers/types.d.ts +29 -0
  390. package/dist/mcp/handlers/types.js +28 -0
  391. package/dist/mcp/server.d.ts +7 -0
  392. package/dist/mcp/server.js +108 -0
  393. package/dist/mcp/smart-router.d.ts +23 -0
  394. package/dist/mcp/smart-router.js +178 -0
  395. package/dist/server/app.d.ts +14 -0
  396. package/dist/server/app.js +632 -0
  397. package/dist/server/auth-store.d.ts +28 -0
  398. package/dist/server/auth-store.js +88 -0
  399. package/dist/server/bull-queues.d.ts +60 -0
  400. package/dist/server/bull-queues.js +90 -0
  401. package/dist/server/email-service.d.ts +55 -0
  402. package/dist/server/email-service.js +291 -0
  403. package/dist/server/job-queue.d.ts +100 -0
  404. package/dist/server/job-queue.js +145 -0
  405. package/dist/server/logger.d.ts +10 -0
  406. package/dist/server/logger.js +37 -0
  407. package/dist/server/middleware/audit-log.d.ts +14 -0
  408. package/dist/server/middleware/audit-log.js +73 -0
  409. package/dist/server/middleware/auth.d.ts +35 -0
  410. package/dist/server/middleware/auth.js +225 -0
  411. package/dist/server/middleware/rate-limit.d.ts +50 -0
  412. package/dist/server/middleware/rate-limit.js +270 -0
  413. package/dist/server/middleware/scope-guard.d.ts +25 -0
  414. package/dist/server/middleware/scope-guard.js +45 -0
  415. package/dist/server/middleware/url-validator.d.ts +15 -0
  416. package/dist/server/middleware/url-validator.js +201 -0
  417. package/dist/server/openapi.yaml +6418 -0
  418. package/dist/server/pg-auth-store.d.ts +146 -0
  419. package/dist/server/pg-auth-store.js +576 -0
  420. package/dist/server/pg-job-queue.d.ts +59 -0
  421. package/dist/server/pg-job-queue.js +375 -0
  422. package/dist/server/routes/activity.d.ts +6 -0
  423. package/dist/server/routes/activity.js +79 -0
  424. package/dist/server/routes/admin-active.d.ts +7 -0
  425. package/dist/server/routes/admin-active.js +120 -0
  426. package/dist/server/routes/admin-stats.d.ts +7 -0
  427. package/dist/server/routes/admin-stats.js +176 -0
  428. package/dist/server/routes/agent.d.ts +24 -0
  429. package/dist/server/routes/agent.js +480 -0
  430. package/dist/server/routes/answer.d.ts +5 -0
  431. package/dist/server/routes/answer.js +125 -0
  432. package/dist/server/routes/ask.d.ts +28 -0
  433. package/dist/server/routes/ask.js +295 -0
  434. package/dist/server/routes/batch.d.ts +6 -0
  435. package/dist/server/routes/batch.js +493 -0
  436. package/dist/server/routes/cache-warm.d.ts +25 -0
  437. package/dist/server/routes/cache-warm.js +212 -0
  438. package/dist/server/routes/cli-usage.d.ts +6 -0
  439. package/dist/server/routes/cli-usage.js +127 -0
  440. package/dist/server/routes/compat.d.ts +23 -0
  441. package/dist/server/routes/compat.js +652 -0
  442. package/dist/server/routes/crawl.d.ts +13 -0
  443. package/dist/server/routes/crawl.js +287 -0
  444. package/dist/server/routes/deep-fetch.d.ts +8 -0
  445. package/dist/server/routes/deep-fetch.js +57 -0
  446. package/dist/server/routes/deep-research.d.ts +11 -0
  447. package/dist/server/routes/deep-research.js +232 -0
  448. package/dist/server/routes/demo.d.ts +24 -0
  449. package/dist/server/routes/demo.js +517 -0
  450. package/dist/server/routes/do.d.ts +8 -0
  451. package/dist/server/routes/do.js +72 -0
  452. package/dist/server/routes/extract.d.ts +14 -0
  453. package/dist/server/routes/extract.js +325 -0
  454. package/dist/server/routes/feed.d.ts +15 -0
  455. package/dist/server/routes/feed.js +311 -0
  456. package/dist/server/routes/fetch-queue.d.ts +13 -0
  457. package/dist/server/routes/fetch-queue.js +357 -0
  458. package/dist/server/routes/fetch.d.ts +7 -0
  459. package/dist/server/routes/fetch.js +1274 -0
  460. package/dist/server/routes/go.d.ts +14 -0
  461. package/dist/server/routes/go.js +81 -0
  462. package/dist/server/routes/health.d.ts +11 -0
  463. package/dist/server/routes/health.js +141 -0
  464. package/dist/server/routes/jobs.d.ts +7 -0
  465. package/dist/server/routes/jobs.js +574 -0
  466. package/dist/server/routes/map.d.ts +11 -0
  467. package/dist/server/routes/map.js +116 -0
  468. package/dist/server/routes/mcp.d.ts +14 -0
  469. package/dist/server/routes/mcp.js +197 -0
  470. package/dist/server/routes/metrics.d.ts +37 -0
  471. package/dist/server/routes/metrics.js +149 -0
  472. package/dist/server/routes/oauth.d.ts +9 -0
  473. package/dist/server/routes/oauth.js +396 -0
  474. package/dist/server/routes/playground.d.ts +17 -0
  475. package/dist/server/routes/playground.js +283 -0
  476. package/dist/server/routes/reader.d.ts +18 -0
  477. package/dist/server/routes/reader.js +192 -0
  478. package/dist/server/routes/research.d.ts +14 -0
  479. package/dist/server/routes/research.js +482 -0
  480. package/dist/server/routes/screenshot.d.ts +22 -0
  481. package/dist/server/routes/screenshot.js +820 -0
  482. package/dist/server/routes/search.d.ts +6 -0
  483. package/dist/server/routes/search.js +874 -0
  484. package/dist/server/routes/session.d.ts +17 -0
  485. package/dist/server/routes/session.js +548 -0
  486. package/dist/server/routes/share.d.ts +18 -0
  487. package/dist/server/routes/share.js +462 -0
  488. package/dist/server/routes/smart-search/handlers/cars.d.ts +2 -0
  489. package/dist/server/routes/smart-search/handlers/cars.js +102 -0
  490. package/dist/server/routes/smart-search/handlers/flights.d.ts +2 -0
  491. package/dist/server/routes/smart-search/handlers/flights.js +72 -0
  492. package/dist/server/routes/smart-search/handlers/general.d.ts +13 -0
  493. package/dist/server/routes/smart-search/handlers/general.js +717 -0
  494. package/dist/server/routes/smart-search/handlers/hotels.d.ts +2 -0
  495. package/dist/server/routes/smart-search/handlers/hotels.js +88 -0
  496. package/dist/server/routes/smart-search/handlers/products.d.ts +2 -0
  497. package/dist/server/routes/smart-search/handlers/products.js +1309 -0
  498. package/dist/server/routes/smart-search/handlers/rental.d.ts +2 -0
  499. package/dist/server/routes/smart-search/handlers/rental.js +154 -0
  500. package/dist/server/routes/smart-search/handlers/restaurants.d.ts +2 -0
  501. package/dist/server/routes/smart-search/handlers/restaurants.js +225 -0
  502. package/dist/server/routes/smart-search/handlers/transit-verdict.d.ts +41 -0
  503. package/dist/server/routes/smart-search/handlers/transit-verdict.js +224 -0
  504. package/dist/server/routes/smart-search/index.d.ts +19 -0
  505. package/dist/server/routes/smart-search/index.js +546 -0
  506. package/dist/server/routes/smart-search/intent.d.ts +3 -0
  507. package/dist/server/routes/smart-search/intent.js +264 -0
  508. package/dist/server/routes/smart-search/llm.d.ts +16 -0
  509. package/dist/server/routes/smart-search/llm.js +70 -0
  510. package/dist/server/routes/smart-search/sources/reddit.d.ts +18 -0
  511. package/dist/server/routes/smart-search/sources/reddit.js +34 -0
  512. package/dist/server/routes/smart-search/sources/yelp.d.ts +25 -0
  513. package/dist/server/routes/smart-search/sources/yelp.js +171 -0
  514. package/dist/server/routes/smart-search/sources/youtube.d.ts +8 -0
  515. package/dist/server/routes/smart-search/sources/youtube.js +9 -0
  516. package/dist/server/routes/smart-search/types.d.ts +81 -0
  517. package/dist/server/routes/smart-search/types.js +1 -0
  518. package/dist/server/routes/smart-search/utils.d.ts +20 -0
  519. package/dist/server/routes/smart-search/utils.js +146 -0
  520. package/dist/server/routes/stats.d.ts +6 -0
  521. package/dist/server/routes/stats.js +71 -0
  522. package/dist/server/routes/stripe.d.ts +15 -0
  523. package/dist/server/routes/stripe.js +296 -0
  524. package/dist/server/routes/transcript-export.d.ts +10 -0
  525. package/dist/server/routes/transcript-export.js +178 -0
  526. package/dist/server/routes/usage.d.ts +9 -0
  527. package/dist/server/routes/usage.js +279 -0
  528. package/dist/server/routes/users.d.ts +8 -0
  529. package/dist/server/routes/users.js +1867 -0
  530. package/dist/server/routes/watch.d.ts +15 -0
  531. package/dist/server/routes/watch.js +309 -0
  532. package/dist/server/routes/webhooks.d.ts +26 -0
  533. package/dist/server/routes/webhooks.js +170 -0
  534. package/dist/server/routes/youtube.d.ts +6 -0
  535. package/dist/server/routes/youtube.js +130 -0
  536. package/dist/server/sentry.d.ts +14 -0
  537. package/dist/server/sentry.js +104 -0
  538. package/dist/server/types.d.ts +15 -0
  539. package/dist/server/types.js +7 -0
  540. package/dist/server/utils/response.d.ts +44 -0
  541. package/dist/server/utils/response.js +69 -0
  542. package/dist/server/utils/sse.d.ts +22 -0
  543. package/dist/server/utils/sse.js +38 -0
  544. package/dist/types.d.ts +552 -0
  545. package/dist/types.js +39 -0
  546. package/llms.txt +105 -0
  547. package/package.json +189 -0
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Playground endpoint — GET /v1/playground?url=<encoded_url>
3
+ * GET /v1/playground/search?q=<query>
4
+ *
5
+ * Unauthenticated endpoints for the WebPeel playground page.
6
+ * Lets visitors try the product without signing up.
7
+ *
8
+ * Security:
9
+ * - CORS-locked to webpeel.dev and localhost
10
+ * - IP-based rate limit: 10 requests per 15 minutes (shared across /fetch and /search)
11
+ * - Simple HTTP-only fetch (no browser rendering)
12
+ * - 5-second timeout
13
+ * - Content truncated to 5,000 chars
14
+ * - No screenshots
15
+ */
16
+ import { Router } from 'express';
17
+ import { peel } from '../../index.js';
18
+ import { getBestSearchProvider } from '../../core/search-provider.js';
19
+ import { createLogger } from '../logger.js';
20
+ const log = createLogger('playground');
21
+ // ── IP-based rate limiter ─────────────────────────────────────────────────────
22
+ const MAX_PER_WINDOW = 10;
23
+ const WINDOW_MS = 15 * 60 * 1000; // 15 minutes
24
+ const ipHits = new Map();
25
+ function checkRateLimit(ip) {
26
+ const now = Date.now();
27
+ const entry = ipHits.get(ip);
28
+ if (!entry || now > entry.resetAt) {
29
+ const resetAt = now + WINDOW_MS;
30
+ ipHits.set(ip, { count: 1, resetAt });
31
+ return { allowed: true, remaining: MAX_PER_WINDOW - 1, resetAt };
32
+ }
33
+ if (entry.count >= MAX_PER_WINDOW) {
34
+ return { allowed: false, remaining: 0, resetAt: entry.resetAt };
35
+ }
36
+ entry.count++;
37
+ return { allowed: true, remaining: MAX_PER_WINDOW - entry.count, resetAt: entry.resetAt };
38
+ }
39
+ // Clean up expired entries every 5 minutes
40
+ setInterval(() => {
41
+ const now = Date.now();
42
+ for (const [ip, entry] of ipHits) {
43
+ if (now > entry.resetAt)
44
+ ipHits.delete(ip);
45
+ }
46
+ }, 5 * 60 * 1000).unref();
47
+ // ── CORS helper ───────────────────────────────────────────────────────────────
48
+ function setCorsHeaders(req, res) {
49
+ const origin = req.headers.origin || '';
50
+ if (origin === 'https://webpeel.dev' ||
51
+ /^http:\/\/localhost(:\d+)?$/.test(origin) ||
52
+ /^http:\/\/127\.0\.0\.1(:\d+)?$/.test(origin)) {
53
+ res.setHeader('Access-Control-Allow-Origin', origin);
54
+ res.setHeader('Vary', 'Origin');
55
+ }
56
+ else if (!origin) {
57
+ // Allow curl and server-to-server (no Origin header)
58
+ res.setHeader('Access-Control-Allow-Origin', '*');
59
+ }
60
+ res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
61
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
62
+ }
63
+ // ── IP extraction ─────────────────────────────────────────────────────────────
64
+ function getClientIp(req) {
65
+ const forwardedFor = req.headers['x-forwarded-for'];
66
+ const firstForwardedIp = typeof forwardedFor === 'string'
67
+ ? forwardedFor.split(',')[0].trim()
68
+ : Array.isArray(forwardedFor) ? forwardedFor[0] : undefined;
69
+ return req.headers['cf-connecting-ip']
70
+ || firstForwardedIp
71
+ || req.headers['x-real-ip']
72
+ || req.ip
73
+ || req.socket?.remoteAddress
74
+ || 'unknown';
75
+ }
76
+ // ── CORS origin check ─────────────────────────────────────────────────────────
77
+ function isAllowedOrigin(req) {
78
+ const origin = req.headers.origin || req.headers.referer || '';
79
+ if (!origin)
80
+ return true; // Allow curl / server-to-server (no Origin header)
81
+ return (origin === 'https://webpeel.dev' ||
82
+ origin.startsWith('https://webpeel.dev/') ||
83
+ /^http:\/\/localhost(:\d+)?/.test(origin) ||
84
+ /^http:\/\/127\.0\.0\.1(:\d+)?/.test(origin));
85
+ }
86
+ // ── Router ────────────────────────────────────────────────────────────────────
87
+ const MAX_CONTENT_LENGTH = 5000;
88
+ const FETCH_TIMEOUT_MS = 5000;
89
+ const SIGN_UP_URL = 'https://app.webpeel.dev';
90
+ export function createPlaygroundRouter() {
91
+ const router = Router();
92
+ // ── CORS preflight ─────────────────────────────────────────────────────────
93
+ router.options('/', (req, res) => {
94
+ setCorsHeaders(req, res);
95
+ res.status(204).end();
96
+ });
97
+ router.options('/search', (req, res) => {
98
+ setCorsHeaders(req, res);
99
+ res.status(204).end();
100
+ });
101
+ // ── GET /v1/playground?url=... ─────────────────────────────────────────────
102
+ router.get('/', async (req, res) => {
103
+ setCorsHeaders(req, res);
104
+ // CORS check
105
+ if (!isAllowedOrigin(req)) {
106
+ res.status(403).json({
107
+ success: false,
108
+ error: {
109
+ type: 'cors_denied',
110
+ message: 'Playground is only available from webpeel.dev',
111
+ hint: `Sign up at ${SIGN_UP_URL} for full API access.`,
112
+ },
113
+ });
114
+ return;
115
+ }
116
+ const url = (req.query.url || '').trim();
117
+ if (!url) {
118
+ res.status(400).json({
119
+ success: false,
120
+ error: {
121
+ type: 'missing_url',
122
+ message: 'URL parameter is required',
123
+ hint: 'GET /v1/playground?url=https://example.com',
124
+ },
125
+ });
126
+ return;
127
+ }
128
+ // Basic URL validation
129
+ let parsedUrl;
130
+ try {
131
+ parsedUrl = new URL(url);
132
+ }
133
+ catch {
134
+ res.status(400).json({
135
+ success: false,
136
+ error: {
137
+ type: 'invalid_url',
138
+ message: 'Invalid URL format',
139
+ hint: 'Ensure the URL is well-formed: https://example.com',
140
+ },
141
+ });
142
+ return;
143
+ }
144
+ if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
145
+ res.status(400).json({
146
+ success: false,
147
+ error: {
148
+ type: 'invalid_url',
149
+ message: 'Only HTTP and HTTPS URLs are allowed',
150
+ },
151
+ });
152
+ return;
153
+ }
154
+ // Rate limit by IP
155
+ const ip = getClientIp(req);
156
+ const rl = checkRateLimit(ip);
157
+ res.setHeader('X-RateLimit-Limit', String(MAX_PER_WINDOW));
158
+ res.setHeader('X-RateLimit-Remaining', String(rl.remaining));
159
+ res.setHeader('X-RateLimit-Reset', String(Math.ceil(rl.resetAt / 1000)));
160
+ if (!rl.allowed) {
161
+ res.status(429).json({
162
+ success: false,
163
+ error: {
164
+ type: 'rate_limited',
165
+ message: 'Playground limit reached (10 requests per 15 minutes)',
166
+ hint: `Sign up for a free API key for unlimited access: ${SIGN_UP_URL}`,
167
+ },
168
+ playground: true,
169
+ });
170
+ return;
171
+ }
172
+ try {
173
+ log.info('Playground fetch', { url, ip });
174
+ const startMs = Date.now();
175
+ const result = await peel(url, {
176
+ timeout: FETCH_TIMEOUT_MS,
177
+ render: false,
178
+ noEscalate: true,
179
+ });
180
+ const fullContent = result.content || '';
181
+ const content = fullContent.slice(0, MAX_CONTENT_LENGTH);
182
+ const truncated = fullContent.length > MAX_CONTENT_LENGTH;
183
+ res.json({
184
+ success: true,
185
+ url: result.url,
186
+ title: result.title,
187
+ content,
188
+ tokens: result.tokens,
189
+ method: result.method,
190
+ elapsed: Date.now() - startMs,
191
+ truncated,
192
+ ...(truncated && {
193
+ upgrade: `Full content available with a free API key → ${SIGN_UP_URL}`,
194
+ }),
195
+ playground: true,
196
+ rateLimitRemaining: rl.remaining,
197
+ });
198
+ }
199
+ catch (err) {
200
+ log.warn('Playground fetch error', { url, error: err?.message });
201
+ res.status(502).json({
202
+ success: false,
203
+ error: {
204
+ type: 'fetch_failed',
205
+ message: err?.message || 'Failed to fetch URL',
206
+ hint: 'Check that the URL is publicly accessible.',
207
+ },
208
+ playground: true,
209
+ });
210
+ }
211
+ });
212
+ // ── GET /v1/playground/search?q=... ───────────────────────────────────────
213
+ router.get('/search', async (req, res) => {
214
+ setCorsHeaders(req, res);
215
+ // CORS check
216
+ if (!isAllowedOrigin(req)) {
217
+ res.status(403).json({
218
+ success: false,
219
+ error: {
220
+ type: 'cors_denied',
221
+ message: 'Playground is only available from webpeel.dev',
222
+ },
223
+ });
224
+ return;
225
+ }
226
+ const q = (req.query.q || '').trim();
227
+ if (!q) {
228
+ res.status(400).json({
229
+ success: false,
230
+ error: {
231
+ type: 'missing_query',
232
+ message: 'Query parameter is required',
233
+ hint: 'GET /v1/playground/search?q=your+query',
234
+ },
235
+ });
236
+ return;
237
+ }
238
+ // Rate limit by IP (shared counter with fetch)
239
+ const ip = getClientIp(req);
240
+ const rl = checkRateLimit(ip);
241
+ res.setHeader('X-RateLimit-Limit', String(MAX_PER_WINDOW));
242
+ res.setHeader('X-RateLimit-Remaining', String(rl.remaining));
243
+ res.setHeader('X-RateLimit-Reset', String(Math.ceil(rl.resetAt / 1000)));
244
+ if (!rl.allowed) {
245
+ res.status(429).json({
246
+ success: false,
247
+ error: {
248
+ type: 'rate_limited',
249
+ message: 'Playground limit reached (10 requests per 15 minutes)',
250
+ hint: `Sign up for a free API key for unlimited access: ${SIGN_UP_URL}`,
251
+ },
252
+ playground: true,
253
+ });
254
+ return;
255
+ }
256
+ try {
257
+ log.info('Playground search', { q, ip });
258
+ const startMs = Date.now();
259
+ const { provider, apiKey } = getBestSearchProvider();
260
+ const results = await provider.searchWeb(q, { count: 5, apiKey });
261
+ res.json({
262
+ success: true,
263
+ query: q,
264
+ results,
265
+ elapsed: Date.now() - startMs,
266
+ playground: true,
267
+ rateLimitRemaining: rl.remaining,
268
+ });
269
+ }
270
+ catch (err) {
271
+ log.warn('Playground search error', { q, error: err?.message });
272
+ res.status(502).json({
273
+ success: false,
274
+ error: {
275
+ type: 'search_failed',
276
+ message: err?.message || 'Search failed',
277
+ },
278
+ playground: true,
279
+ });
280
+ }
281
+ });
282
+ return router;
283
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Zero-auth URL-prefix reader API — Jina Reader style
3
+ *
4
+ * GET /r/https://example.com → returns markdown (no auth, no signup)
5
+ * GET /s/search+query → searches web and returns fetched results
6
+ *
7
+ * Headers (Jina-compatible):
8
+ * x-respond-with: markdown | text | html | screenshot
9
+ * x-timeout: seconds (default 10, max 15)
10
+ * x-target-selector: CSS selector to target
11
+ * x-wait-for-selector: CSS selector to wait for (triggers browser rendering)
12
+ * x-with-generated-alt: true to caption images
13
+ * x-no-cache: true to bypass cache
14
+ *
15
+ * Rate limit: 20 requests per 15 minutes per IP (no auth required)
16
+ */
17
+ import { Router } from 'express';
18
+ export declare function createReaderRouter(): Router;
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Zero-auth URL-prefix reader API — Jina Reader style
3
+ *
4
+ * GET /r/https://example.com → returns markdown (no auth, no signup)
5
+ * GET /s/search+query → searches web and returns fetched results
6
+ *
7
+ * Headers (Jina-compatible):
8
+ * x-respond-with: markdown | text | html | screenshot
9
+ * x-timeout: seconds (default 10, max 15)
10
+ * x-target-selector: CSS selector to target
11
+ * x-wait-for-selector: CSS selector to wait for (triggers browser rendering)
12
+ * x-with-generated-alt: true to caption images
13
+ * x-no-cache: true to bypass cache
14
+ *
15
+ * Rate limit: 20 requests per 15 minutes per IP (no auth required)
16
+ */
17
+ import { Router } from 'express';
18
+ import { peel } from '../../index.js';
19
+ import { createLogger } from '../../core/logger.js';
20
+ import { validateUrlForSSRF, SSRFError } from '../middleware/url-validator.js';
21
+ import crypto from 'crypto';
22
+ const log = createLogger('reader');
23
+ // IP-based rate limiting (same approach as playground)
24
+ const ipHits = new Map();
25
+ const RATE_LIMIT = 20; // 20 requests per window
26
+ const RATE_WINDOW = 15 * 60 * 1000; // 15 minutes
27
+ function checkRateLimit(ip) {
28
+ const now = Date.now();
29
+ const entry = ipHits.get(ip);
30
+ if (!entry || entry.resetAt < now) {
31
+ ipHits.set(ip, { count: 1, resetAt: now + RATE_WINDOW });
32
+ return { allowed: true, remaining: RATE_LIMIT - 1 };
33
+ }
34
+ entry.count++;
35
+ if (entry.count > RATE_LIMIT) {
36
+ return { allowed: false, remaining: 0 };
37
+ }
38
+ return { allowed: true, remaining: RATE_LIMIT - entry.count };
39
+ }
40
+ export function createReaderRouter() {
41
+ const router = Router();
42
+ // GET /r/https://example.com — fetch any URL, return markdown
43
+ // Also supports /r/http://example.com
44
+ router.get('/r/*', async (req, res) => {
45
+ // Express wildcard: req.params[0] gives everything after /r/
46
+ const targetUrl = req.params[0] || req.url.replace(/^\/r\//, '');
47
+ if (!targetUrl || !/^https?:\/\//i.test(targetUrl)) {
48
+ return res.status(400).json({
49
+ success: false,
50
+ error: {
51
+ type: 'invalid_url',
52
+ message: 'Prepend /r/ to a full URL. Example: /r/https://example.com',
53
+ },
54
+ });
55
+ }
56
+ // SECURITY: SSRF validation — block private IPs, localhost, etc.
57
+ try {
58
+ validateUrlForSSRF(targetUrl);
59
+ }
60
+ catch (err) {
61
+ if (err instanceof SSRFError) {
62
+ return res.status(400).json({
63
+ success: false,
64
+ error: { type: 'ssrf_blocked', message: err.message },
65
+ });
66
+ }
67
+ throw err;
68
+ }
69
+ const ip = req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
70
+ req.ip ||
71
+ 'unknown';
72
+ const { allowed, remaining } = checkRateLimit(ip);
73
+ res.setHeader('X-RateLimit-Remaining', remaining.toString());
74
+ if (!allowed) {
75
+ return res.status(429).json({
76
+ success: false,
77
+ error: {
78
+ type: 'rate_limited',
79
+ message: 'Rate limit exceeded. 20 requests per 15 minutes without auth.',
80
+ },
81
+ });
82
+ }
83
+ try {
84
+ // Parse request headers for options (Jina-compatible)
85
+ const format = req.headers['x-respond-with'] || 'markdown';
86
+ const timeoutSec = parseInt(req.headers['x-timeout'] || '10', 10);
87
+ const timeout = Math.min(timeoutSec * 1000, 15000); // cap at 15s
88
+ const targetSelector = req.headers['x-target-selector'];
89
+ const waitForSelector = req.headers['x-wait-for-selector'];
90
+ const withCaptions = req.headers['x-with-generated-alt'] === 'true';
91
+ const result = await peel(targetUrl, {
92
+ timeout,
93
+ render: !!waitForSelector || !!targetSelector,
94
+ noEscalate: !waitForSelector, // no browser escalation unless explicitly needed
95
+ captionImages: withCaptions,
96
+ selector: targetSelector,
97
+ waitSelector: waitForSelector,
98
+ });
99
+ // Cache-Control: this endpoint is public and heavily cacheable.
100
+ // Cloudflare edge caches for 2 min; serves stale for up to 10 min while revalidating.
101
+ res.setHeader('Cache-Control', 'public, s-maxage=120, stale-while-revalidate=600');
102
+ // Vary on Accept so different content-type representations are cached separately.
103
+ res.setHeader('Vary', 'Accept');
104
+ // Return based on format
105
+ const responseFormat = format.toLowerCase();
106
+ if (responseFormat === 'text') {
107
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
108
+ return res.send(result.content || '');
109
+ }
110
+ else if (responseFormat === 'html') {
111
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
112
+ return res.send(result.html || result.rawHtml || '');
113
+ }
114
+ else if (responseFormat === 'screenshot') {
115
+ return res.json({
116
+ success: true,
117
+ screenshot: result.screenshot || null,
118
+ url: targetUrl,
119
+ });
120
+ }
121
+ else {
122
+ // Default: markdown as plain text (like Jina)
123
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
124
+ const header = `Title: ${result.title || ''}\nURL: ${targetUrl}\nTokens: ${result.tokens || 0}\n\n`;
125
+ return res.send(header + (result.content || ''));
126
+ }
127
+ }
128
+ catch (err) {
129
+ log.error('Reader error:', err.message);
130
+ return res.status(500).json({
131
+ success: false,
132
+ error: { type: 'fetch_failed', message: err.message },
133
+ requestId: crypto.randomUUID(),
134
+ });
135
+ }
136
+ });
137
+ // GET /s/query — search web and return fetched results
138
+ router.get('/s/*', async (req, res) => {
139
+ const query = decodeURIComponent(req.params[0] || '');
140
+ if (!query.trim()) {
141
+ return res.status(400).json({
142
+ success: false,
143
+ error: {
144
+ type: 'missing_query',
145
+ message: 'Prepend /s/ to your search query. Example: /s/stripe pricing plans',
146
+ },
147
+ });
148
+ }
149
+ const ip = req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
150
+ req.ip ||
151
+ 'unknown';
152
+ const { allowed, remaining } = checkRateLimit(ip);
153
+ res.setHeader('X-RateLimit-Remaining', remaining.toString());
154
+ if (!allowed) {
155
+ return res.status(429).json({
156
+ success: false,
157
+ error: { type: 'rate_limited', message: 'Rate limit exceeded.' },
158
+ });
159
+ }
160
+ try {
161
+ const { getBestSearchProvider } = await import('../../core/search-provider.js');
162
+ const { provider, apiKey } = await getBestSearchProvider();
163
+ const results = await provider.searchWeb(query, { count: 5, apiKey });
164
+ // Fetch top 3 results (5K char limit each to keep responses manageable)
165
+ const fetched = await Promise.all(results.slice(0, 3).map(async (r) => {
166
+ try {
167
+ const page = await peel(r.url, { timeout: 5000, noEscalate: true });
168
+ return {
169
+ title: r.title || page.title,
170
+ url: r.url,
171
+ content: (page.content || '').slice(0, 5000),
172
+ };
173
+ }
174
+ catch {
175
+ return { title: r.title, url: r.url, content: r.snippet || '' };
176
+ }
177
+ }));
178
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
179
+ const output = fetched
180
+ .map((r, i) => `## Result ${i + 1}: ${r.title}\nURL: ${r.url}\n\n${r.content}`)
181
+ .join('\n\n---\n\n');
182
+ return res.send(`Search: ${query}\nResults: ${fetched.length}\n\n${output}`);
183
+ }
184
+ catch (err) {
185
+ return res.status(500).json({
186
+ success: false,
187
+ error: { type: 'search_failed', message: err.message },
188
+ });
189
+ }
190
+ });
191
+ return router;
192
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * POST /v1/research
3
+ *
4
+ * Lightweight research endpoint that chains search → fetch → compile.
5
+ * Default: uses WebPeel's self-hosted LLM (Ollama on Hetzner) for synthesis.
6
+ * Override: users can pass their own LLM config (BYOK) via the `llm` body param.
7
+ *
8
+ * Auth: API key required (full or read scope)
9
+ * Body: ResearchRequest
10
+ */
11
+ import { Router } from 'express';
12
+ export declare function expandQuery(query: string): string[];
13
+ export declare function extractKeyFacts(content: string, query: string, maxFacts?: number): string[];
14
+ export declare function createResearchRouter(): Router;