@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,840 @@
1
+ /**
2
+ * Interact commands: ask, webask, watch, diff, track, summarize, agent, answer, research, schemas
3
+ */
4
+ import ora from 'ora';
5
+ import { peel, cleanup } from '../../index.js';
6
+ import { loadConfig } from '../../cli-auth.js';
7
+ import { writeStdout, formatRelativeTime } from '../utils.js';
8
+ import { runFetch } from './fetch.js';
9
+ import { SCHEMA_TEMPLATES, getSchemaTemplate, listSchemaTemplates } from '../../core/schema-templates.js';
10
+ export function registerInteractCommands(program) {
11
+ // ── ask subcommand (question mode) ────────────────────────────────────────
12
+ program
13
+ .command('ask <url> <question>')
14
+ .description('Ask a question about any page')
15
+ .option('--json', 'Output as JSON')
16
+ .option('-s, --silent', 'Silent mode')
17
+ .action(async (url, question, opts) => {
18
+ await runFetch(url, {
19
+ ...opts,
20
+ question,
21
+ readable: true,
22
+ });
23
+ });
24
+ // ── watch command ─────────────────────────────────────────────────────────
25
+ program
26
+ .command('watch <url>')
27
+ .description('Monitor a URL for changes and assertion failures')
28
+ .option('--interval <duration>', 'Check interval (e.g. 30s, 5m, 1h)', '5m')
29
+ .option('--assert <condition...>', 'Assertion(s) to check (e.g. "status=200" "body.health=ok")')
30
+ .option('--webhook <url>', 'POST this URL on assertion failure or content change')
31
+ .option('-t, --timeout <ms>', 'Per-request timeout in ms', (v) => parseInt(v, 10), 10000)
32
+ .option('--max-checks <n>', 'Stop after N checks (default: unlimited)', (v) => parseInt(v, 10))
33
+ .option('--json', 'Output each check as NDJSON to stdout')
34
+ .option('-s, --silent', 'Only output on failures/changes')
35
+ .option('-r, --render', 'Use browser rendering for checks')
36
+ .action(async (url, options) => {
37
+ const { watch: runWatch, parseDuration, parseAssertion } = await import('../../core/watch.js');
38
+ // Validate URL
39
+ try {
40
+ const parsed = new URL(url);
41
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
42
+ console.error('Error: Only HTTP and HTTPS protocols are allowed');
43
+ process.exit(1);
44
+ }
45
+ }
46
+ catch {
47
+ console.error(`Error: Invalid URL format: ${url}`);
48
+ process.exit(1);
49
+ }
50
+ // Parse interval
51
+ let intervalMs;
52
+ try {
53
+ intervalMs = parseDuration(options.interval);
54
+ }
55
+ catch (e) {
56
+ console.error(`Error: ${e.message}`);
57
+ process.exit(1);
58
+ }
59
+ // Parse assertions
60
+ const assertions = [];
61
+ if (options.assert && Array.isArray(options.assert)) {
62
+ for (const expr of options.assert) {
63
+ try {
64
+ assertions.push(parseAssertion(expr));
65
+ }
66
+ catch (e) {
67
+ console.error(`Error: ${e.message}`);
68
+ process.exit(1);
69
+ }
70
+ }
71
+ }
72
+ if (!options.json && !options.silent) {
73
+ const intervalLabel = options.interval;
74
+ const assertLabel = assertions.length > 0
75
+ ? ` with ${assertions.length} assertion(s)`
76
+ : '';
77
+ process.stderr.write(`Watching ${url} every ${intervalLabel}${assertLabel}. Press Ctrl+C to stop.\n`);
78
+ }
79
+ const watchOptions = {
80
+ url,
81
+ intervalMs,
82
+ assertions,
83
+ webhookUrl: options.webhook,
84
+ timeout: options.timeout,
85
+ maxChecks: options.maxChecks,
86
+ render: options.render || false,
87
+ json: options.json || false,
88
+ silent: options.silent || false,
89
+ };
90
+ try {
91
+ await runWatch(watchOptions);
92
+ }
93
+ catch (error) {
94
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
95
+ process.exit(1);
96
+ }
97
+ process.exit(0);
98
+ });
99
+ // ── diff command ──────────────────────────────────────────────────────────
100
+ program
101
+ .command('diff <url>')
102
+ .description('Show semantic diff between current content and the last tracked snapshot')
103
+ .option('--last', 'Compare against last tracked snapshot (default)')
104
+ .option('--against <snapshot-url>', 'Compare against the snapshot stored for a different URL')
105
+ .option('--fields <fields>', 'For JSON responses: only diff these fields (comma-separated dot-notation)')
106
+ .option('--json', 'Output diff as JSON')
107
+ .option('-r, --render', 'Use browser rendering')
108
+ .option('-t, --timeout <ms>', 'Request timeout in ms', (v) => parseInt(v, 10), 30000)
109
+ .option('-s, --silent', 'Silent mode (no spinner)')
110
+ .action(async (url, options) => {
111
+ const isJson = options.json;
112
+ // Validate URL
113
+ try {
114
+ const parsed = new URL(url);
115
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
116
+ if (isJson) {
117
+ await writeStdout(JSON.stringify({ success: false, error: { type: 'invalid_url', message: 'Only HTTP and HTTPS protocols are allowed' } }) + '\n');
118
+ }
119
+ else {
120
+ console.error('Error: Only HTTP and HTTPS protocols are allowed');
121
+ }
122
+ process.exit(1);
123
+ }
124
+ }
125
+ catch {
126
+ if (isJson) {
127
+ await writeStdout(JSON.stringify({ success: false, error: { type: 'invalid_url', message: `Invalid URL format: ${url}` } }) + '\n');
128
+ }
129
+ else {
130
+ console.error(`Error: Invalid URL format: ${url}`);
131
+ }
132
+ process.exit(1);
133
+ }
134
+ const spinner = options.silent ? null : ora('Fetching and diffing...').start();
135
+ try {
136
+ const { diffUrl } = await import('../../core/diff.js');
137
+ const fields = options.fields
138
+ ? options.fields.split(',').map((f) => f.trim()).filter(Boolean)
139
+ : undefined;
140
+ const result = await diffUrl(url, {
141
+ render: options.render || false,
142
+ timeout: options.timeout,
143
+ fields,
144
+ });
145
+ if (spinner) {
146
+ spinner.succeed(`Diff completed in ${result.changed ? 'CHANGED' : 'no change'}`);
147
+ }
148
+ if (isJson) {
149
+ await writeStdout(JSON.stringify(result, null, 2) + '\n');
150
+ }
151
+ else {
152
+ // Human-readable output
153
+ const ago = result.previousTimestamp
154
+ ? formatRelativeTime(new Date(result.previousTimestamp))
155
+ : 'unknown';
156
+ console.log(`\nComparing ${result.url} (now vs ${ago})\n`);
157
+ if (!result.changed) {
158
+ console.log(' No changes detected.');
159
+ }
160
+ else {
161
+ for (const change of result.changes) {
162
+ const label = change.field ?? change.path ?? '(unknown)';
163
+ if (change.type === 'modified') {
164
+ console.log(` Modified: ${label} ${change.before} → ${change.after}`);
165
+ }
166
+ else if (change.type === 'added') {
167
+ console.log(` Added: ${label} ${change.after}`);
168
+ }
169
+ else if (change.type === 'removed') {
170
+ console.log(` Removed: ${label} ${change.before}`);
171
+ }
172
+ }
173
+ }
174
+ console.log(`\nSummary: ${result.summary}`);
175
+ }
176
+ await cleanup();
177
+ process.exit(0);
178
+ }
179
+ catch (error) {
180
+ if (spinner)
181
+ spinner.fail('Diff failed');
182
+ if (isJson) {
183
+ await writeStdout(JSON.stringify({
184
+ error: error instanceof Error ? error.message : 'Unknown error',
185
+ code: 'FETCH_FAILED',
186
+ }) + '\n');
187
+ }
188
+ else {
189
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
190
+ }
191
+ await cleanup();
192
+ process.exit(1);
193
+ }
194
+ });
195
+ // ── track command ─────────────────────────────────────────────────────────
196
+ program
197
+ .command('track <url>')
198
+ .description('Track changes on a URL (saves snapshot for use with `webpeel diff`)')
199
+ .option('-s, --silent', 'Silent mode (no spinner)')
200
+ .option('--json', 'Output as JSON')
201
+ .option('-r, --render', 'Use browser rendering')
202
+ .action(async (url, options) => {
203
+ const spinner = options.silent ? null : ora('Fetching and tracking...').start();
204
+ try {
205
+ // changeTracking: true saves the snapshot to ~/.webpeel/snapshots/ so that
206
+ // `webpeel diff` can compare against it later.
207
+ const result = await peel(url, {
208
+ render: options.render || false,
209
+ changeTracking: true,
210
+ });
211
+ if (spinner) {
212
+ spinner.succeed(`Tracked in ${result.elapsed}ms`);
213
+ }
214
+ const changeStatus = result.changeTracking?.changeStatus ?? 'new';
215
+ const previousScrapeAt = result.changeTracking?.previousScrapeAt ?? null;
216
+ if (options.json) {
217
+ await writeStdout(JSON.stringify({
218
+ url: result.url,
219
+ title: result.title,
220
+ fingerprint: result.fingerprint,
221
+ tokens: result.tokens,
222
+ contentType: result.contentType,
223
+ changeStatus,
224
+ previousScrapeAt,
225
+ lastChecked: new Date().toISOString(),
226
+ }, null, 2) + '\n');
227
+ }
228
+ else {
229
+ console.log(`URL: ${result.url}`);
230
+ console.log(`Title: ${result.title}`);
231
+ console.log(`Fingerprint: ${result.fingerprint}`);
232
+ console.log(`Tokens: ${result.tokens}`);
233
+ console.log(`Status: ${changeStatus}`);
234
+ if (previousScrapeAt)
235
+ console.log(`Previous check: ${previousScrapeAt}`);
236
+ console.log(`Last checked: ${new Date().toISOString()}`);
237
+ console.log('\nSnapshot saved. Run `webpeel diff <url> --last` to compare future changes.');
238
+ }
239
+ await cleanup();
240
+ process.exit(0);
241
+ }
242
+ catch (error) {
243
+ if (spinner)
244
+ spinner.fail('Tracking failed');
245
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
246
+ await cleanup();
247
+ process.exit(1);
248
+ }
249
+ });
250
+ // ── summarize command ─────────────────────────────────────────────────────
251
+ program
252
+ .command('summarize <url>')
253
+ .description('Generate an AI-powered summary of a URL')
254
+ .option('--llm-key <key>', 'LLM API key (or use OPENAI_API_KEY env var)')
255
+ .option('--llm-model <model>', 'LLM model to use (default: gpt-4o-mini)')
256
+ .option('--llm-base-url <url>', 'LLM API base URL (default: https://api.openai.com/v1)')
257
+ .option('--prompt <prompt>', 'Custom summary prompt')
258
+ .option('-s, --silent', 'Silent mode (no spinner)')
259
+ .option('--json', 'Output as JSON')
260
+ .action(async (url, options) => {
261
+ const llmApiKey = options.llmKey || process.env.OPENAI_API_KEY;
262
+ if (!llmApiKey) {
263
+ console.error('Error: --llm-key or OPENAI_API_KEY environment variable is required');
264
+ process.exit(1);
265
+ }
266
+ const spinner = options.silent ? null : ora('Fetching and summarizing...').start();
267
+ try {
268
+ const result = await peel(url, {
269
+ extract: {
270
+ prompt: options.prompt || 'Summarize this webpage in 2-3 sentences.',
271
+ llmApiKey,
272
+ llmModel: options.llmModel || 'gpt-4o-mini',
273
+ llmBaseUrl: options.llmBaseUrl || 'https://api.openai.com/v1',
274
+ },
275
+ });
276
+ if (spinner) {
277
+ spinner.succeed(`Summarized in ${result.elapsed}ms`);
278
+ }
279
+ if (options.json) {
280
+ console.log(JSON.stringify({
281
+ url: result.url,
282
+ title: result.title,
283
+ summary: result.extracted,
284
+ }, null, 2));
285
+ }
286
+ else {
287
+ console.log(`\n${result.title}\n`);
288
+ console.log(result.extracted);
289
+ }
290
+ await cleanup();
291
+ process.exit(0);
292
+ }
293
+ catch (error) {
294
+ if (spinner)
295
+ spinner.fail('Summary generation failed');
296
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
297
+ await cleanup();
298
+ process.exit(1);
299
+ }
300
+ });
301
+ // ── agent command ─────────────────────────────────────────────────────────
302
+ program
303
+ .command('agent <prompt>')
304
+ .description('Web research agent — LLM-free by default, add --llm-key for AI synthesis')
305
+ .option('--llm-key <key>', 'LLM API key (or use OPENAI_API_KEY env var)')
306
+ .option('--llm-model <model>', 'LLM model to use (default: gpt-4o-mini)')
307
+ .option('--llm-base-url <url>', 'LLM API base URL')
308
+ .option('--urls <urls>', 'Comma-separated starting URLs')
309
+ .option('--max-pages <n>', 'Maximum pages to visit (default: 10)', '10')
310
+ .option('--schema <json>', 'Schema template name (e.g. product, article) or JSON schema for structured output')
311
+ .option('-s, --silent', 'Silent mode (no spinner)')
312
+ .option('--json', 'Output as JSON')
313
+ .option('--stream', 'Stream progress via SSE (calls API endpoint, requires API key)')
314
+ .action(async (prompt, options) => {
315
+ const llmApiKey = options.llmKey || process.env.OPENAI_API_KEY;
316
+ const urls = options.urls ? options.urls.split(',').map((u) => u.trim()) : undefined;
317
+ // Parse schema (support templates)
318
+ let schema;
319
+ if (options.schema) {
320
+ const template = getSchemaTemplate(options.schema);
321
+ if (template) {
322
+ schema = template.fields;
323
+ }
324
+ else {
325
+ try {
326
+ schema = JSON.parse(options.schema);
327
+ }
328
+ catch {
329
+ console.error(`Error: --schema must be a template name (${listSchemaTemplates().join(', ')}) or valid JSON`);
330
+ process.exit(1);
331
+ }
332
+ }
333
+ }
334
+ if (llmApiKey) {
335
+ // Full LLM agent mode (existing code)
336
+ const spinner = options.silent ? null : ora('Running agent research...').start();
337
+ try {
338
+ const { runAgent } = await import('../../core/agent.js');
339
+ const result = await runAgent({
340
+ prompt,
341
+ urls,
342
+ schema,
343
+ llmApiKey,
344
+ llmModel: options.llmModel,
345
+ llmApiBase: options.llmBaseUrl,
346
+ maxPages: parseInt(options.maxPages, 10),
347
+ onProgress: (progress) => {
348
+ if (spinner)
349
+ spinner.text = progress.message;
350
+ },
351
+ });
352
+ if (spinner)
353
+ spinner.succeed(`Agent finished: ${result.pagesVisited} pages`);
354
+ if (options.json) {
355
+ console.log(JSON.stringify(result, null, 2));
356
+ }
357
+ else {
358
+ console.log(`\nSources (${result.sources.length}):`);
359
+ result.sources.forEach(s => console.log(` • ${s}`));
360
+ console.log(`\nResults:`);
361
+ console.log(JSON.stringify(result.data, null, 2));
362
+ }
363
+ await cleanup();
364
+ process.exit(0);
365
+ }
366
+ catch (e) {
367
+ if (spinner)
368
+ spinner.fail('Agent failed');
369
+ console.error(e instanceof Error ? e.message : e);
370
+ await cleanup();
371
+ process.exit(1);
372
+ }
373
+ }
374
+ else {
375
+ // LLM-free mode: search + fetch + BM25 extraction
376
+ const spinner = options.silent ? null : ora('Running LLM-free research...').start();
377
+ try {
378
+ // Import needed modules
379
+ const { quickAnswer } = await import('../../core/quick-answer.js');
380
+ // Step 1: Get URLs to process
381
+ let targetUrls = urls || [];
382
+ // If no URLs, search the web
383
+ if (targetUrls.length === 0) {
384
+ if (spinner)
385
+ spinner.text = 'Searching the web...';
386
+ try {
387
+ const { getBestSearchProvider } = await import('../../core/search-provider.js');
388
+ const { provider, apiKey: searchApiKey } = getBestSearchProvider();
389
+ const searchResults = await provider.searchWeb(prompt, {
390
+ count: Math.min(parseInt(options.maxPages, 10) || 5, 10),
391
+ apiKey: searchApiKey,
392
+ });
393
+ targetUrls = searchResults.map((r) => r.url);
394
+ }
395
+ catch {
396
+ // Fallback: try DuckDuckGo HTML
397
+ if (spinner)
398
+ spinner.text = 'Searching via DuckDuckGo...';
399
+ try {
400
+ const duckUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(prompt)}`;
401
+ const searchResult = await peel(duckUrl, { budget: 4000 });
402
+ // Extract URLs from search results content
403
+ const urlMatches = searchResult.content.match(/https?:\/\/[^\s\)]+/g) || [];
404
+ targetUrls = urlMatches
405
+ .filter((u) => !u.includes('duckduckgo.com'))
406
+ .slice(0, parseInt(options.maxPages, 10) || 5);
407
+ }
408
+ catch {
409
+ // No search results
410
+ }
411
+ }
412
+ }
413
+ if (targetUrls.length === 0) {
414
+ if (spinner)
415
+ spinner.fail('No URLs found. Provide --urls or a more specific prompt.');
416
+ process.exit(1);
417
+ }
418
+ if (spinner)
419
+ spinner.text = `Processing ${targetUrls.length} pages...`;
420
+ // Step 2: Fetch and extract from each URL
421
+ const results = [];
422
+ for (const url of targetUrls) {
423
+ try {
424
+ if (spinner)
425
+ spinner.text = `Fetching: ${url.substring(0, 60)}...`;
426
+ const pageResult = await peel(url, { budget: 4000 });
427
+ let extracted = null;
428
+ let confidence = 0;
429
+ if (schema) {
430
+ // Extract each schema field using smartExtractSchemaFields
431
+ const { smartExtractSchemaFields: smartExtractResearch } = await import('../../core/schema-postprocess.js');
432
+ extracted = smartExtractResearch(pageResult.content, schema, quickAnswer, {
433
+ pageTitle: pageResult.title,
434
+ pageUrl: url,
435
+ metadata: pageResult.metadata,
436
+ });
437
+ // Calculate confidence from quickAnswer for any field
438
+ for (const question of Object.values(schema)) {
439
+ try {
440
+ const qa = quickAnswer({ content: pageResult.content, question: typeof question === 'string' ? question : '' });
441
+ confidence = Math.max(confidence, qa.confidence || 0);
442
+ }
443
+ catch { /* ignore */ }
444
+ break; // just need one confidence estimate
445
+ }
446
+ }
447
+ else {
448
+ // Answer the prompt directly
449
+ try {
450
+ const qa = quickAnswer({ content: pageResult.content, question: prompt });
451
+ extracted = { answer: qa.answer || '' };
452
+ confidence = qa.confidence || 0;
453
+ }
454
+ catch {
455
+ extracted = null;
456
+ }
457
+ }
458
+ results.push({
459
+ url,
460
+ title: pageResult.metadata?.title || url,
461
+ extracted,
462
+ content: pageResult.content.substring(0, 500),
463
+ confidence,
464
+ });
465
+ }
466
+ catch (e) {
467
+ // Skip failed URLs
468
+ if (process.env.DEBUG) {
469
+ console.debug('[webpeel]', `Failed to fetch ${url}:`, e instanceof Error ? e.message : e);
470
+ }
471
+ }
472
+ }
473
+ if (spinner)
474
+ spinner.succeed(`Processed ${results.length}/${targetUrls.length} pages (LLM-free)`);
475
+ if (options.json) {
476
+ console.log(JSON.stringify({
477
+ mode: 'llm-free',
478
+ prompt,
479
+ schema: schema || null,
480
+ results,
481
+ sources: results.map(r => r.url),
482
+ pagesVisited: results.length,
483
+ }, null, 2));
484
+ }
485
+ else {
486
+ console.log(`\nšŸ“Š Results (${results.length} pages, LLM-free):\n`);
487
+ for (const r of results) {
488
+ console.log(`── ${r.title} ──`);
489
+ console.log(` ${r.url}`);
490
+ if (r.extracted) {
491
+ for (const [k, v] of Object.entries(r.extracted)) {
492
+ if (v)
493
+ console.log(` ${k}: ${v}`);
494
+ }
495
+ }
496
+ console.log(` Confidence: ${(r.confidence * 100).toFixed(0)}%\n`);
497
+ }
498
+ }
499
+ await cleanup();
500
+ process.exit(0);
501
+ }
502
+ catch (e) {
503
+ if (spinner)
504
+ spinner.fail('Research failed');
505
+ console.error(e instanceof Error ? e.message : e);
506
+ await cleanup();
507
+ process.exit(1);
508
+ }
509
+ }
510
+ });
511
+ // ── webask command ────────────────────────────────────────────────────────
512
+ program
513
+ .command('webask <question>')
514
+ .alias('ask-web')
515
+ .description('Search the web and get a direct answer (no LLM key required)')
516
+ .option('-n, --sources <n>', 'Number of sources to check (1-5, default 3)', '3')
517
+ .option('--json', 'Output as JSON')
518
+ .option('-s, --silent', 'Silent mode')
519
+ .action(async (question, options) => {
520
+ const isJson = !!options.json;
521
+ const isSilent = !!options.silent;
522
+ const numSources = Math.min(Math.max(parseInt(options.sources) || 3, 1), 5);
523
+ const askCfg = loadConfig();
524
+ const askApiKey = askCfg.apiKey || process.env.WEBPEEL_API_KEY;
525
+ const askApiUrl = process.env.WEBPEEL_API_URL || 'https://api.webpeel.dev';
526
+ if (!askApiKey) {
527
+ console.error('No API key configured. Run: webpeel auth <your-key>');
528
+ console.error('Get a free key at: https://app.webpeel.dev/keys');
529
+ process.exit(2);
530
+ }
531
+ let spinner = null;
532
+ if (!isSilent && !isJson) {
533
+ const { default: oraModule } = await import('ora');
534
+ spinner = oraModule(`Searching for: ${question}`).start();
535
+ }
536
+ try {
537
+ const params = new URLSearchParams({ q: question, sources: String(numSources) });
538
+ const res = await fetch(`${askApiUrl}/v1/ask?${params}`, {
539
+ headers: { Authorization: `Bearer ${askApiKey}` },
540
+ signal: AbortSignal.timeout(60000),
541
+ });
542
+ if (res.status === 401) {
543
+ if (spinner)
544
+ spinner.fail('API key invalid or expired. Run: webpeel auth <new-key>');
545
+ process.exit(2);
546
+ }
547
+ if (res.status === 404) {
548
+ if (spinner)
549
+ spinner.fail('Ask endpoint not available on this server version');
550
+ process.exit(1);
551
+ }
552
+ if (!res.ok) {
553
+ const body = await res.text().catch(() => '');
554
+ if (spinner)
555
+ spinner.fail(`API error ${res.status}: ${body.slice(0, 100)}`);
556
+ process.exit(1);
557
+ }
558
+ const data = await res.json();
559
+ if (spinner) {
560
+ if (data.answer) {
561
+ spinner.succeed(`Found answer (confidence: ${Math.round((data.confidence || 0) * 100)}%)`);
562
+ }
563
+ else {
564
+ spinner.warn('No confident answer found');
565
+ }
566
+ }
567
+ if (isJson) {
568
+ console.log(JSON.stringify(data, null, 2));
569
+ }
570
+ else {
571
+ if (data.answer) {
572
+ console.log('\n' + data.answer);
573
+ if (data.sources?.length && !isSilent) {
574
+ console.log('\nSources:');
575
+ data.sources.slice(0, 3).forEach((s) => console.log(` • ${s.title || s.url} — ${s.url}`));
576
+ }
577
+ }
578
+ else {
579
+ console.log('\nNo confident answer found for:', question);
580
+ }
581
+ if (data.elapsed && !isSilent)
582
+ console.log(`\n⚔ ${data.elapsed}ms`);
583
+ }
584
+ }
585
+ catch (err) {
586
+ if (spinner)
587
+ spinner.fail(err.message);
588
+ process.exit(1);
589
+ }
590
+ });
591
+ // ── answer command ────────────────────────────────────────────────────────
592
+ program
593
+ .command('answer <question>')
594
+ .description('Ask a question, search the web, and get an AI-generated answer with citations (BYOK)')
595
+ .option('--provider <provider>', 'Search provider: duckduckgo (default) or brave')
596
+ .option('--search-api-key <key>', 'Search provider API key (or env WEBPEEL_BRAVE_API_KEY)')
597
+ .option('--llm <provider>', 'LLM provider: openai, anthropic, or google (required)')
598
+ .option('--llm-api-key <key>', 'LLM API key (or env OPENAI_API_KEY / ANTHROPIC_API_KEY / GOOGLE_API_KEY)')
599
+ .option('--llm-model <model>', 'LLM model name (optional, uses provider default)')
600
+ .option('--max-sources <n>', 'Maximum sources to fetch (1-10, default 5)', '5')
601
+ .option('--json', 'Output as JSON')
602
+ .option('-s, --silent', 'Silent mode')
603
+ .action(async (question, options) => {
604
+ const spinner = options.silent ? null : ora('Thinking...').start();
605
+ try {
606
+ const { answerQuestion } = await import('../../core/answer.js');
607
+ const config = loadConfig();
608
+ const llmProvider = options.llm;
609
+ if (!llmProvider || !['openai', 'anthropic', 'google'].includes(llmProvider)) {
610
+ console.error('Error: --llm is required (openai, anthropic, or google)');
611
+ process.exit(1);
612
+ }
613
+ const llmApiKey = options.llmApiKey
614
+ || process.env.OPENAI_API_KEY
615
+ || process.env.ANTHROPIC_API_KEY
616
+ || process.env.GOOGLE_API_KEY
617
+ || '';
618
+ if (!llmApiKey) {
619
+ console.error('Error: --llm-api-key is required (or set OPENAI_API_KEY / ANTHROPIC_API_KEY / GOOGLE_API_KEY)');
620
+ process.exit(1);
621
+ }
622
+ const searchProvider = (options.provider || 'duckduckgo');
623
+ const searchApiKey = options.searchApiKey
624
+ || process.env.WEBPEEL_BRAVE_API_KEY
625
+ || config.braveApiKey
626
+ || undefined;
627
+ const maxSources = Math.min(Math.max(parseInt(options.maxSources) || 5, 1), 10);
628
+ if (spinner)
629
+ spinner.text = 'Searching the web...';
630
+ const result = await answerQuestion({
631
+ question,
632
+ searchProvider,
633
+ searchApiKey,
634
+ llmProvider,
635
+ llmApiKey,
636
+ llmModel: options.llmModel,
637
+ maxSources,
638
+ stream: false,
639
+ });
640
+ if (spinner)
641
+ spinner.succeed('Done');
642
+ if (options.json) {
643
+ const jsonStr = JSON.stringify(result, null, 2);
644
+ await new Promise((resolve, reject) => {
645
+ process.stdout.write(jsonStr + '\n', (err) => {
646
+ if (err)
647
+ reject(err);
648
+ else
649
+ resolve();
650
+ });
651
+ });
652
+ }
653
+ else {
654
+ console.log(`\n${result.answer}`);
655
+ console.log(`\nSources:`);
656
+ result.citations.forEach((c, i) => {
657
+ console.log(` [${i + 1}] ${c.title}`);
658
+ console.log(` ${c.url}`);
659
+ });
660
+ console.log(`\nModel: ${result.llmModel} (${result.llmProvider})`);
661
+ }
662
+ await cleanup();
663
+ process.exit(0);
664
+ }
665
+ catch (error) {
666
+ if (spinner)
667
+ spinner.fail('Answer generation failed');
668
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
669
+ await cleanup();
670
+ process.exit(1);
671
+ }
672
+ });
673
+ // ── research command ──────────────────────────────────────────────────────
674
+ program
675
+ .command('research <query>')
676
+ .description('Conduct autonomous multi-step web research on a topic and synthesize a report')
677
+ .option('--max-sources <n>', 'Maximum sources to consult (default: 5)', '5')
678
+ .option('--max-depth <n>', 'Link-following depth (default: 1)', '1')
679
+ .option('--format <f>', 'Output format: report (default) or sources', 'report')
680
+ .option('--llm-key <key>', 'LLM API key for synthesis (or env OPENAI_API_KEY)')
681
+ .option('--llm-model <model>', 'LLM model for synthesis (default: gpt-4o-mini)')
682
+ .option('--llm-base-url <url>', 'LLM API base URL (default: https://api.openai.com/v1)')
683
+ .option('--timeout <ms>', 'Max research time in ms (default: 40000)', '60000')
684
+ .option('--json', 'Output result as JSON')
685
+ .option('-s, --silent', 'Suppress progress output')
686
+ .action(async (query, options) => {
687
+ const isSilent = !!options.silent;
688
+ const isJson = !!options.json;
689
+ const maxSources = parseInt(options.maxSources) || 5;
690
+ const maxDepth = parseInt(options.maxDepth) || 1;
691
+ const timeout = parseInt(options.timeout) || 60000;
692
+ const outputFormat = options.format === 'sources' ? 'sources' : 'report';
693
+ const apiKey = options.llmKey || process.env.OPENAI_API_KEY;
694
+ const model = options.llmModel;
695
+ const baseUrl = options.llmBaseUrl;
696
+ const phaseIcons = {
697
+ searching: 'šŸ”',
698
+ fetching: 'šŸ“„',
699
+ extracting: '🧠',
700
+ following: 'šŸ”—',
701
+ synthesizing: 'āœļø',
702
+ };
703
+ try {
704
+ const { research } = await import('../../core/research.js');
705
+ const result = await research({
706
+ query,
707
+ maxSources,
708
+ maxDepth,
709
+ timeout,
710
+ outputFormat: outputFormat,
711
+ apiKey,
712
+ model,
713
+ baseUrl,
714
+ onProgress: (step) => {
715
+ if (!isSilent && !isJson) {
716
+ const icon = phaseIcons[step.phase] ?? 'āš™ļø';
717
+ const extra = step.sourcesFound !== undefined
718
+ ? ` (found ${step.sourcesFound})`
719
+ : step.sourcesFetched !== undefined
720
+ ? ` (${step.sourcesFetched} fetched)`
721
+ : '';
722
+ process.stderr.write(`${icon} ${step.message}${extra}...\n`);
723
+ }
724
+ },
725
+ });
726
+ if (isJson) {
727
+ await writeStdout(JSON.stringify(result, null, 2) + '\n');
728
+ }
729
+ else {
730
+ await writeStdout(result.report + '\n');
731
+ if (!isSilent) {
732
+ const elapsed = (result.elapsed / 1000).toFixed(1);
733
+ const cost = result.cost !== undefined ? ` | cost: $${result.cost.toFixed(4)}` : '';
734
+ process.stderr.write(`\nšŸ“Š ${result.sourcesConsulted} sources consulted (${result.totalSourcesFound} found) | ${elapsed}s${cost}\n`);
735
+ }
736
+ }
737
+ await cleanup();
738
+ process.exit(0);
739
+ }
740
+ catch (error) {
741
+ const msg = error instanceof Error ? error.message : 'Unknown error';
742
+ if (isJson) {
743
+ await writeStdout(JSON.stringify({ success: false, error: { type: 'fetch_failed', message: msg } }) + '\n');
744
+ }
745
+ else {
746
+ console.error(`\nError: ${msg}`);
747
+ }
748
+ await cleanup();
749
+ process.exit(1);
750
+ }
751
+ });
752
+ // ── do command — natural language intent routing ──────────────────────────
753
+ program
754
+ .command('do <task...>')
755
+ .description('Do anything — describe what you want in plain English')
756
+ .option('-s, --silent', 'Silent mode')
757
+ .option('--json', 'JSON output')
758
+ .action(async (taskParts, options) => {
759
+ const task = taskParts.join(' ');
760
+ const cfg = loadConfig();
761
+ const apiKey = cfg.apiKey || process.env.WEBPEEL_API_KEY;
762
+ const apiUrl = process.env.WEBPEEL_API_URL || 'https://api.webpeel.dev';
763
+ if (!apiKey) {
764
+ console.error('No API key. Run: webpeel auth <key>');
765
+ process.exit(1);
766
+ }
767
+ let spinner = null;
768
+ if (!options.silent && !options.json) {
769
+ const { default: oraModule } = await import('ora');
770
+ spinner = oraModule(`Doing: ${task}`).start();
771
+ }
772
+ try {
773
+ const res = await fetch(`${apiUrl}/v1/do`, {
774
+ method: 'POST',
775
+ headers: {
776
+ Authorization: `Bearer ${apiKey}`,
777
+ 'Content-Type': 'application/json',
778
+ },
779
+ body: JSON.stringify({ task }),
780
+ signal: AbortSignal.timeout(60000),
781
+ });
782
+ const data = await res.json();
783
+ if (spinner) {
784
+ if (data.error) {
785
+ spinner.fail(`Failed: ${data.message || data.error}`);
786
+ }
787
+ else {
788
+ spinner.succeed(`Done (${data.elapsed}ms) — intent: ${data.intent}`);
789
+ }
790
+ }
791
+ if (options.json) {
792
+ console.log(JSON.stringify(data, null, 2));
793
+ }
794
+ else {
795
+ if (data.error) {
796
+ console.error(`Error: ${data.message || data.error}`);
797
+ process.exit(1);
798
+ }
799
+ console.log(`Intent: ${data.intent}`);
800
+ if (data.url)
801
+ console.log(`URL: ${data.url}`);
802
+ if (data.query)
803
+ console.log(`Query: ${data.query}`);
804
+ console.log(`Elapsed: ${data.elapsed}ms`);
805
+ console.log('');
806
+ // Pretty-print the result
807
+ const result = data.result || {};
808
+ if (result.content)
809
+ console.log(result.content.slice(0, 2000));
810
+ else if (result.answer)
811
+ console.log(`Answer: ${result.answer}\nConfidence: ${Math.round((result.confidence || 0) * 100)}%`);
812
+ else if (result.screenshot)
813
+ console.log(`Screenshot: ${result.screenshot.length} bytes (base64)`);
814
+ else
815
+ console.log(JSON.stringify(result, null, 2).slice(0, 2000));
816
+ }
817
+ }
818
+ catch (err) {
819
+ if (spinner)
820
+ spinner.fail(err.message);
821
+ if (!options.silent)
822
+ console.error(`Error: ${err.message}`);
823
+ process.exit(1);
824
+ }
825
+ });
826
+ // ── schemas command ───────────────────────────────────────────────────────
827
+ program
828
+ .command('schemas')
829
+ .description('List available extraction schema templates')
830
+ .action(() => {
831
+ console.log('\nAvailable schema templates:\n');
832
+ for (const [key, template] of Object.entries(SCHEMA_TEMPLATES)) {
833
+ console.log(` ${key.padEnd(12)} ${template.description}`);
834
+ console.log(` ${''.padEnd(12)} Fields: ${Object.keys(template.fields).join(', ')}`);
835
+ console.log('');
836
+ }
837
+ console.log('Usage: webpeel "https://example.com" --schema product');
838
+ console.log(' webpeel "https://example.com" --schema \'{"field":"description"}\'');
839
+ });
840
+ }