@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,145 @@
1
+ /**
2
+ * Job queue for async operations
3
+ *
4
+ * Factory creates PostgreSQL-backed queue in production or in-memory queue for local dev.
5
+ * Tracks crawl, batch scrape, and extraction jobs with progress updates.
6
+ */
7
+ import { randomUUID } from 'crypto';
8
+ import { PostgresJobQueue } from './pg-job-queue.js';
9
+ import { createLogger } from './logger.js';
10
+ const log = createLogger('job-queue');
11
+ /**
12
+ * In-memory job queue for local development
13
+ */
14
+ export class InMemoryJobQueue {
15
+ jobs = new Map();
16
+ cleanupInterval;
17
+ constructor() {
18
+ // Clean expired jobs every hour
19
+ this.cleanupInterval = setInterval(() => {
20
+ this.cleanExpired();
21
+ }, 60 * 60 * 1000);
22
+ }
23
+ /**
24
+ * Create a new job
25
+ */
26
+ createJob(type, webhook, ownerId) {
27
+ const now = new Date().toISOString();
28
+ const job = {
29
+ id: randomUUID(),
30
+ type,
31
+ status: 'queued',
32
+ progress: 0,
33
+ total: 0,
34
+ completed: 0,
35
+ creditsUsed: 0,
36
+ data: [],
37
+ webhook,
38
+ ownerId,
39
+ createdAt: now,
40
+ updatedAt: now,
41
+ expiresAt: new Date(Date.now() + 25 * 60 * 60 * 1000).toISOString(), // 25h from now (updated on completion)
42
+ };
43
+ this.jobs.set(job.id, job);
44
+ return job;
45
+ }
46
+ /**
47
+ * Get a job by ID
48
+ */
49
+ getJob(id) {
50
+ return this.jobs.get(id) || null;
51
+ }
52
+ /**
53
+ * Update a job
54
+ */
55
+ updateJob(id, update) {
56
+ const job = this.jobs.get(id);
57
+ if (!job)
58
+ return;
59
+ Object.assign(job, update, {
60
+ updatedAt: new Date().toISOString(),
61
+ });
62
+ // When job completes/fails, set expiration to 24h from now
63
+ if (update.status === 'completed' || update.status === 'failed') {
64
+ job.expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
65
+ }
66
+ // Update progress percentage
67
+ if (job.total > 0) {
68
+ job.progress = Math.round((job.completed / job.total) * 100);
69
+ }
70
+ this.jobs.set(id, job);
71
+ }
72
+ /**
73
+ * Cancel a job
74
+ */
75
+ cancelJob(id) {
76
+ const job = this.jobs.get(id);
77
+ if (!job)
78
+ return false;
79
+ // Can only cancel queued or processing jobs
80
+ if (job.status !== 'queued' && job.status !== 'processing') {
81
+ return false;
82
+ }
83
+ this.updateJob(id, {
84
+ status: 'cancelled',
85
+ expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
86
+ });
87
+ return true;
88
+ }
89
+ /**
90
+ * List jobs with optional filters
91
+ */
92
+ listJobs(options) {
93
+ let jobs = Array.from(this.jobs.values());
94
+ if (options?.ownerId) {
95
+ jobs = jobs.filter(j => j.ownerId === options.ownerId);
96
+ }
97
+ if (options?.type) {
98
+ jobs = jobs.filter(j => j.type === options.type);
99
+ }
100
+ if (options?.status) {
101
+ jobs = jobs.filter(j => j.status === options.status);
102
+ }
103
+ // Sort by creation time (newest first)
104
+ jobs.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
105
+ if (options?.limit) {
106
+ jobs = jobs.slice(0, options.limit);
107
+ }
108
+ return jobs;
109
+ }
110
+ /**
111
+ * Remove expired jobs
112
+ */
113
+ cleanExpired() {
114
+ const now = Date.now();
115
+ for (const [id, job] of this.jobs.entries()) {
116
+ if (new Date(job.expiresAt).getTime() < now) {
117
+ this.jobs.delete(id);
118
+ }
119
+ }
120
+ }
121
+ /**
122
+ * Clean up interval on shutdown
123
+ */
124
+ destroy() {
125
+ clearInterval(this.cleanupInterval);
126
+ }
127
+ }
128
+ /**
129
+ * Create job queue based on environment
130
+ * - Uses PostgreSQL if DATABASE_URL is set
131
+ * - Falls back to in-memory for local development
132
+ */
133
+ export function createJobQueue() {
134
+ if (process.env.DATABASE_URL) {
135
+ log.info('Using PostgreSQL job queue');
136
+ return new PostgresJobQueue();
137
+ }
138
+ else {
139
+ log.info('Using in-memory job queue');
140
+ return new InMemoryJobQueue();
141
+ }
142
+ }
143
+ // Legacy global instance for backwards compatibility (deprecated)
144
+ // @deprecated Use createJobQueue() instead
145
+ export const jobQueue = new InMemoryJobQueue();
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Lightweight structured logger — no external dependencies.
3
+ * Respects LOG_LEVEL env var: debug | info | warn | error (default: info)
4
+ */
5
+ export declare function createLogger(component: string): {
6
+ debug(msg: string, data?: Record<string, unknown>): void;
7
+ info(msg: string, data?: Record<string, unknown>): void;
8
+ warn(msg: string, data?: Record<string, unknown>): void;
9
+ error(msg: string, data?: Record<string, unknown>): void;
10
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Lightweight structured logger — no external dependencies.
3
+ * Respects LOG_LEVEL env var: debug | info | warn | error (default: info)
4
+ */
5
+ const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
6
+ const LOG_LEVEL = (process.env.LOG_LEVEL ?? 'info');
7
+ function shouldLog(level) {
8
+ return LEVELS[level] >= (LEVELS[LOG_LEVEL] ?? LEVELS.info);
9
+ }
10
+ function formatLog(level, component, message, data) {
11
+ const ts = new Date().toISOString();
12
+ const base = `${ts} [${level.toUpperCase()}] [${component}] ${message}`;
13
+ if (data && Object.keys(data).length > 0) {
14
+ return `${base} ${JSON.stringify(data)}`;
15
+ }
16
+ return base;
17
+ }
18
+ export function createLogger(component) {
19
+ return {
20
+ debug(msg, data) {
21
+ if (shouldLog('debug'))
22
+ console.debug(formatLog('debug', component, msg, data));
23
+ },
24
+ info(msg, data) {
25
+ if (shouldLog('info'))
26
+ console.info(formatLog('info', component, msg, data));
27
+ },
28
+ warn(msg, data) {
29
+ if (shouldLog('warn'))
30
+ console.warn(formatLog('warn', component, msg, data));
31
+ },
32
+ error(msg, data) {
33
+ if (shouldLog('error'))
34
+ console.error(formatLog('error', component, msg, data));
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * WebPeel Audit Logging Middleware
3
+ *
4
+ * Records who accessed which API endpoints and the outcome.
5
+ * Designed to be privacy-safe:
6
+ * - Logs userId, keyId, method, path, status, duration, IP, user-agent
7
+ * - Does NOT log: request bodies, auth headers, query params (may contain API keys)
8
+ * - Only logs /v1/ endpoints (skips health checks, static files)
9
+ *
10
+ * When DATABASE_URL is set, also writes to usage_logs table (fire-and-forget).
11
+ */
12
+ import { Request, Response, NextFunction } from 'express';
13
+ import '../types.js';
14
+ export declare function auditMiddleware(req: Request, res: Response, next: NextFunction): void;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * WebPeel Audit Logging Middleware
3
+ *
4
+ * Records who accessed which API endpoints and the outcome.
5
+ * Designed to be privacy-safe:
6
+ * - Logs userId, keyId, method, path, status, duration, IP, user-agent
7
+ * - Does NOT log: request bodies, auth headers, query params (may contain API keys)
8
+ * - Only logs /v1/ endpoints (skips health checks, static files)
9
+ *
10
+ * When DATABASE_URL is set, also writes to usage_logs table (fire-and-forget).
11
+ */
12
+ import '../types.js'; // Augments Express.Request with requestId
13
+ import { createLogger } from '../logger.js';
14
+ import pg from 'pg';
15
+ const auditLog = createLogger('audit');
16
+ // ── Singleton audit DB pool ───────────────────────────────────────────────────
17
+ // One small pool shared by all requests — created lazily on first /v1/ request.
18
+ let _auditPool = null;
19
+ function getAuditPool() {
20
+ if (!_auditPool && process.env.DATABASE_URL) {
21
+ _auditPool = new pg.Pool({
22
+ connectionString: process.env.DATABASE_URL,
23
+ ssl: process.env.DATABASE_URL.includes('sslmode=require')
24
+ ? { rejectUnauthorized: process.env.PG_REJECT_UNAUTHORIZED !== 'false' }
25
+ : undefined,
26
+ max: 3,
27
+ idleTimeoutMillis: 30_000,
28
+ });
29
+ }
30
+ return _auditPool;
31
+ }
32
+ export function auditMiddleware(req, res, next) {
33
+ const startTime = Date.now();
34
+ res.on('finish', () => {
35
+ const duration = Date.now() - startTime;
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ const auth = req.auth;
38
+ const userId = auth?.keyInfo?.accountId || 'anonymous';
39
+ // Use a truncated prefix of the key as a safe identifier (never log the full key)
40
+ const rawKey = auth?.keyInfo?.key;
41
+ const keyId = rawKey ? rawKey.slice(0, 8) + '...' : 'none';
42
+ const clientIp = req.headers['cf-connecting-ip'] ||
43
+ req.headers['x-forwarded-for'] ||
44
+ req.ip;
45
+ // Only log API endpoints (skip health checks, static files)
46
+ if (req.path.startsWith('/v1/')) {
47
+ auditLog.info(`${req.method} ${req.path}`, {
48
+ action: `${req.method} ${req.path}`,
49
+ userId,
50
+ keyId,
51
+ statusCode: res.statusCode,
52
+ duration,
53
+ ip: clientIp,
54
+ userAgent: req.headers['user-agent']?.slice(0, 100),
55
+ // DO NOT log: request body, auth headers, query params (may contain API keys or secrets)
56
+ });
57
+ // ── Fire-and-forget DB write ────────────────────────────────────────────
58
+ // Only runs when DATABASE_URL is set. Never slows down the response.
59
+ if (process.env.DATABASE_URL) {
60
+ const dbUserId = userId !== 'anonymous' ? userId : null;
61
+ const urlParam = typeof req.query?.url === 'string' ? req.query.url.slice(0, 2048) : null;
62
+ getAuditPool()
63
+ .query(`INSERT INTO usage_logs
64
+ (user_id, url, endpoint, method, status_code, processing_time_ms, ip_address, created_at)
65
+ VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())`, [dbUserId, urlParam, req.path, req.method, res.statusCode, duration, clientIp])
66
+ .catch(() => {
67
+ // Ignore audit DB failures — never propagate to the request
68
+ });
69
+ }
70
+ }
71
+ });
72
+ next();
73
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * API key authentication middleware with SOFT LIMIT enforcement
3
+ *
4
+ * Philosophy: Never fully block users. When weekly limits are exceeded,
5
+ * degrade to HTTP-only mode instead of returning 429.
6
+ * BURST limits (hourly) are HARD limits and return 429.
7
+ *
8
+ * Dual auth: Accepts both API keys AND JWT session tokens.
9
+ * API keys are validated via the auth store; JWTs are verified with JWT_SECRET.
10
+ * Dashboard pages use JWT tokens; CLI/SDK users use API keys.
11
+ */
12
+ import { Request, Response, NextFunction } from 'express';
13
+ import { AuthStore, ApiKeyInfo } from '../auth-store.js';
14
+ import { KeyScope } from '../pg-auth-store.js';
15
+ import '../types.js';
16
+ declare global {
17
+ namespace Express {
18
+ interface Request {
19
+ auth?: {
20
+ keyInfo: ApiKeyInfo | null;
21
+ tier: 'free' | 'starter' | 'pro' | 'enterprise' | 'max' | 'admin';
22
+ rateLimit: number;
23
+ softLimited: boolean;
24
+ extraUsageAvailable: boolean;
25
+ };
26
+ /**
27
+ * Permission scope of the authenticated API key.
28
+ * Undefined when authenticated via JWT (dashboard session) — JWT users bypass scope enforcement.
29
+ * Set to 'full' | 'read' | 'restricted' for API key requests.
30
+ */
31
+ keyScope?: KeyScope;
32
+ }
33
+ }
34
+ }
35
+ export declare function createAuthMiddleware(authStore: AuthStore): (req: Request, res: Response, next: NextFunction) => Promise<void>;
@@ -0,0 +1,225 @@
1
+ /**
2
+ * API key authentication middleware with SOFT LIMIT enforcement
3
+ *
4
+ * Philosophy: Never fully block users. When weekly limits are exceeded,
5
+ * degrade to HTTP-only mode instead of returning 429.
6
+ * BURST limits (hourly) are HARD limits and return 429.
7
+ *
8
+ * Dual auth: Accepts both API keys AND JWT session tokens.
9
+ * API keys are validated via the auth store; JWTs are verified with JWT_SECRET.
10
+ * Dashboard pages use JWT tokens; CLI/SDK users use API keys.
11
+ */
12
+ import jwt from 'jsonwebtoken';
13
+ import { PostgresAuthStore } from '../pg-auth-store.js';
14
+ import '../types.js'; // Augments Express.Request with requestId
15
+ export function createAuthMiddleware(authStore) {
16
+ return async (req, res, next) => {
17
+ try {
18
+ // Extract API key from Authorization header or X-API-Key header
19
+ const authHeader = req.headers.authorization;
20
+ const apiKeyHeader = req.headers['x-api-key'];
21
+ // SECURITY: Skip API key auth for public/JWT-protected endpoints
22
+ // These routes either need no auth or use their own JWT middleware
23
+ const isPublicEndpoint = req.path === '/health' || req.path === '/ready' ||
24
+ req.path.startsWith('/v1/auth/') ||
25
+ req.path === '/v1/webhooks/stripe' ||
26
+ req.path === '/v1/me' ||
27
+ req.path.startsWith('/v1/keys') ||
28
+ req.path.startsWith('/v1/extra-usage');
29
+ if (isPublicEndpoint) {
30
+ req.auth = {
31
+ keyInfo: null,
32
+ tier: 'free',
33
+ rateLimit: 10,
34
+ softLimited: false,
35
+ extraUsageAvailable: false,
36
+ };
37
+ return next();
38
+ }
39
+ // SECURITY: Ensure headers are strings before processing (guards against
40
+ // malformed multi-value headers that would cause a null reference crash → 502)
41
+ const rawAuth = typeof authHeader === 'string' ? authHeader : undefined;
42
+ const rawApiKey = typeof apiKeyHeader === 'string' ? apiKeyHeader : undefined;
43
+ let apiKey = null;
44
+ if (rawAuth?.startsWith('Bearer ')) {
45
+ apiKey = rawAuth.slice(7);
46
+ }
47
+ else if (rawApiKey) {
48
+ apiKey = rawApiKey;
49
+ }
50
+ if (!apiKey) {
51
+ // No credentials supplied — let the request through unauthenticated.
52
+ // Public routes (/health, /v1/auth/*) already returned above.
53
+ // Protected routes must check req.auth?.keyInfo and return 401 themselves.
54
+ req.auth = undefined;
55
+ return next();
56
+ }
57
+ // Validate API key if provided
58
+ let keyInfo = null;
59
+ let softLimited = false;
60
+ let extraUsageAvailable = false;
61
+ if (apiKey) {
62
+ keyInfo = await authStore.validateKey(apiKey);
63
+ if (!keyInfo) {
64
+ // Check if key exists but is expired (before falling through to JWT)
65
+ if (authStore instanceof PostgresAuthStore) {
66
+ const expired = await authStore.isKeyExpired(apiKey);
67
+ if (expired) {
68
+ res.status(401).json({
69
+ success: false,
70
+ error: {
71
+ type: 'key_expired',
72
+ message: 'This API key has expired.',
73
+ hint: 'Generate a new key at https://app.webpeel.dev/keys',
74
+ docs: 'https://webpeel.dev/docs/errors#unauthorized',
75
+ },
76
+ metadata: { requestId: req.requestId },
77
+ });
78
+ return;
79
+ }
80
+ }
81
+ // API key not found — try JWT session token as fallback.
82
+ // Dashboard uses JWT tokens (from /v1/auth/login or /v1/auth/oauth),
83
+ // while CLI/SDK users use API keys. Support both seamlessly.
84
+ const jwtSecret = process.env.JWT_SECRET;
85
+ if (jwtSecret) {
86
+ try {
87
+ const payload = jwt.verify(apiKey, jwtSecret);
88
+ if (payload.userId) {
89
+ // Valid JWT — treat as authenticated session user.
90
+ // Set req.auth with null keyInfo (no API key) and attach
91
+ // the JWT payload so routes can use req.user.userId.
92
+ req.auth = {
93
+ keyInfo: null,
94
+ tier: payload.tier || 'free',
95
+ rateLimit: 25,
96
+ softLimited: false,
97
+ extraUsageAvailable: false,
98
+ };
99
+ req.user = payload;
100
+ return next();
101
+ }
102
+ }
103
+ catch (e) {
104
+ if (process.env.DEBUG)
105
+ console.debug('[webpeel]', 'jwt verify failed:', e instanceof Error ? e.message : e);
106
+ }
107
+ }
108
+ // Firecrawl compat routes expect { error: "string" } not { error: { type, message } }
109
+ const isCompatRoute = /^\/(v[12]\/(scrape|crawl|map|search))/.test(req.path);
110
+ if (isCompatRoute) {
111
+ res.status(401).json({
112
+ success: false,
113
+ error: 'Invalid or expired API key. Get one at https://app.webpeel.dev/keys',
114
+ });
115
+ }
116
+ else {
117
+ res.status(401).json({
118
+ success: false,
119
+ error: {
120
+ type: 'invalid_key',
121
+ message: 'Invalid or expired API key.',
122
+ hint: 'Check your key at https://app.webpeel.dev/keys or generate a new one.',
123
+ docs: 'https://webpeel.dev/docs/errors#unauthorized',
124
+ },
125
+ metadata: { requestId: req.requestId },
126
+ });
127
+ }
128
+ return;
129
+ }
130
+ // Check limits (only for PostgresAuthStore, skip for admin tier)
131
+ if (authStore instanceof PostgresAuthStore && keyInfo?.tier !== 'admin') {
132
+ // HARD LIMIT: Check burst limit first (per-hour cap)
133
+ const { allowed: burstAllowed, burst } = await authStore.checkBurstLimit(apiKey);
134
+ if (!burstAllowed) {
135
+ // Burst limit exceeded - HARD 429 with Retry-After
136
+ const retryAfterSeconds = 60 * parseInt(burst.resetsIn.match(/\d+/)?.[0] || '1', 10);
137
+ res.setHeader('Retry-After', retryAfterSeconds.toString());
138
+ res.setHeader('X-Burst-Limit', burst.limit.toString());
139
+ res.setHeader('X-Burst-Used', burst.count.toString());
140
+ const tier = keyInfo?.tier || 'free';
141
+ const upgradeHint = tier === 'free'
142
+ ? ' Upgrade to Pro ($9/mo) for 100/hr → https://webpeel.dev/pricing'
143
+ : tier === 'pro'
144
+ ? ' Upgrade to Max ($29/mo) for 500/hr → https://webpeel.dev/pricing'
145
+ : '';
146
+ res.status(429).json({
147
+ success: false,
148
+ error: {
149
+ type: 'burst_limit_exceeded',
150
+ message: `Hourly burst limit exceeded (${burst.count}/${burst.limit} on ${tier} plan). Resets in ${burst.resetsIn}.`,
151
+ hint: `Retry after ${burst.resetsIn}.${upgradeHint}`,
152
+ docs: 'https://webpeel.dev/docs/errors#rate-limited',
153
+ },
154
+ metadata: { requestId: req.requestId },
155
+ });
156
+ return;
157
+ }
158
+ // Add burst headers
159
+ res.setHeader('X-Burst-Limit', burst.limit.toString());
160
+ res.setHeader('X-Burst-Used', burst.count.toString());
161
+ res.setHeader('X-Burst-Remaining', burst.remaining.toString());
162
+ // SOFT LIMIT: Check weekly usage
163
+ const { allowed, usage } = await authStore.checkLimit(apiKey);
164
+ // Check if extra usage is available
165
+ if (!allowed) {
166
+ extraUsageAvailable = await authStore.canUseExtraUsage(apiKey);
167
+ if (!extraUsageAvailable) {
168
+ // Over weekly quota, no extra usage — SOFT LIMIT (degrade to HTTP-only)
169
+ softLimited = true;
170
+ res.setHeader('X-Soft-Limited', 'true');
171
+ res.setHeader('X-Soft-Limit-Reason', 'Weekly quota exceeded. Requests degraded to HTTP-only mode.');
172
+ res.setHeader('X-Upgrade-URL', 'https://webpeel.dev/pricing');
173
+ }
174
+ }
175
+ // Add weekly usage headers
176
+ if (usage) {
177
+ res.setHeader('X-Weekly-Limit', usage.totalAvailable.toString());
178
+ res.setHeader('X-Weekly-Used', usage.totalUsed.toString());
179
+ res.setHeader('X-Weekly-Remaining', Math.max(0, usage.remaining).toString());
180
+ res.setHeader('X-Weekly-Percent', usage.percentUsed.toString());
181
+ res.setHeader('X-Weekly-Resets-At', usage.resetsAt);
182
+ // Warn if over 80% usage and not using extra usage
183
+ if (usage.percentUsed >= 80 && !softLimited && !extraUsageAvailable) {
184
+ res.setHeader('X-Usage-Warning', `You've used ${usage.percentUsed}% of your weekly quota. Consider upgrading at https://webpeel.dev/pricing`);
185
+ }
186
+ }
187
+ // Add extra usage headers if available
188
+ const extraInfo = await authStore.getExtraUsageInfo(apiKey);
189
+ if (extraInfo) {
190
+ res.setHeader('X-Extra-Usage-Enabled', extraInfo.enabled ? 'true' : 'false');
191
+ res.setHeader('X-Extra-Usage-Balance', extraInfo.balance.toFixed(2));
192
+ if (extraInfo.enabled) {
193
+ res.setHeader('X-Extra-Usage-Spent', extraInfo.spent.toFixed(2));
194
+ res.setHeader('X-Extra-Usage-Limit', extraInfo.spendingLimit.toFixed(2));
195
+ }
196
+ }
197
+ }
198
+ }
199
+ // Set auth context on request
200
+ req.auth = {
201
+ keyInfo,
202
+ tier: keyInfo?.tier || 'free',
203
+ rateLimit: keyInfo?.rateLimit || 10,
204
+ softLimited,
205
+ extraUsageAvailable,
206
+ };
207
+ // Attach API key scope (only for API key auth; JWT users get undefined = bypass scope checks)
208
+ if (keyInfo) {
209
+ req.keyScope = keyInfo.scope || 'full';
210
+ }
211
+ next();
212
+ }
213
+ catch (_error) {
214
+ res.status(500).json({
215
+ success: false,
216
+ error: {
217
+ type: 'internal_error',
218
+ message: 'Authentication failed',
219
+ docs: 'https://webpeel.dev/docs/errors#internal-error',
220
+ },
221
+ metadata: { requestId: req.requestId },
222
+ });
223
+ }
224
+ };
225
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Redis-backed rate limiting middleware.
3
+ *
4
+ * Uses `rate-limiter-flexible` for atomic Redis operations — works correctly
5
+ * across all 6 K8s API pods (shared state via Redis).
6
+ *
7
+ * Falls back to in-memory when REDIS_URL is not set (CLI/local dev).
8
+ *
9
+ * Features:
10
+ * - Per-key (API key or IP) rate limiting
11
+ * - Tier-based limits: free=50/hr, pro=100/hr, max=500/hr
12
+ * - Route-based cost weighting: crawl=5x, render=3x, batch/screenshot=2x
13
+ * - Per-IP rate limiting ON TOP of API key limits (abuse prevention)
14
+ * - Exempt paths for health/docs endpoints
15
+ */
16
+ import { Request, Response, NextFunction } from 'express';
17
+ import '../types.js';
18
+ export declare class RateLimiter {
19
+ private keyLimiter;
20
+ private ipLimiter;
21
+ private windowMs;
22
+ private initialized;
23
+ private initPromise;
24
+ constructor(windowMs?: number);
25
+ private init;
26
+ waitForInit(): Promise<void>;
27
+ /**
28
+ * Check if request is allowed.
29
+ * Returns { allowed, remaining, retryAfter? }
30
+ */
31
+ checkLimit(identifier: string, limit: number, cost?: number): Promise<{
32
+ allowed: boolean;
33
+ remaining: number;
34
+ retryAfter?: number;
35
+ }>;
36
+ /**
37
+ * Check per-IP limit (global cap regardless of API key).
38
+ */
39
+ checkIpLimit(ip: string, cost?: number): Promise<{
40
+ allowed: boolean;
41
+ remaining: number;
42
+ retryAfter?: number;
43
+ }>;
44
+ /**
45
+ * Backward compat: cleanup is no-op for Redis (TTL handles it).
46
+ * Kept for in-memory fallback and for app.ts which calls it on an interval.
47
+ */
48
+ cleanup(): void;
49
+ }
50
+ export declare function createRateLimitMiddleware(limiter: RateLimiter): (req: Request, res: Response, next: NextFunction) => Promise<void>;