@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.
- package/LICENSE +15 -0
- package/README.md +313 -0
- package/dist/cache.d.ts +30 -0
- package/dist/cache.js +139 -0
- package/dist/cli/commands/auth.d.ts +5 -0
- package/dist/cli/commands/auth.js +411 -0
- package/dist/cli/commands/doctor.d.ts +37 -0
- package/dist/cli/commands/doctor.js +371 -0
- package/dist/cli/commands/fetch.d.ts +6 -0
- package/dist/cli/commands/fetch.js +1345 -0
- package/dist/cli/commands/guide.d.ts +2 -0
- package/dist/cli/commands/guide.js +183 -0
- package/dist/cli/commands/interact.d.ts +5 -0
- package/dist/cli/commands/interact.js +840 -0
- package/dist/cli/commands/jobs.d.ts +5 -0
- package/dist/cli/commands/jobs.js +997 -0
- package/dist/cli/commands/monitor.d.ts +12 -0
- package/dist/cli/commands/monitor.js +197 -0
- package/dist/cli/commands/observe.d.ts +12 -0
- package/dist/cli/commands/observe.js +158 -0
- package/dist/cli/commands/screenshot.d.ts +5 -0
- package/dist/cli/commands/screenshot.js +282 -0
- package/dist/cli/commands/search.d.ts +5 -0
- package/dist/cli/commands/search.js +1021 -0
- package/dist/cli/commands/setup.d.ts +13 -0
- package/dist/cli/commands/setup.js +244 -0
- package/dist/cli/commands/skill.d.ts +15 -0
- package/dist/cli/commands/skill.js +195 -0
- package/dist/cli/utils.d.ts +84 -0
- package/dist/cli/utils.js +806 -0
- package/dist/cli-auth.d.ts +75 -0
- package/dist/cli-auth.js +369 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +99 -0
- package/dist/core/actions.d.ts +69 -0
- package/dist/core/actions.js +495 -0
- package/dist/core/agent.d.ts +98 -0
- package/dist/core/agent.js +558 -0
- package/dist/core/answer.d.ts +42 -0
- package/dist/core/answer.js +395 -0
- package/dist/core/application-tracker.d.ts +84 -0
- package/dist/core/application-tracker.js +184 -0
- package/dist/core/apply.d.ts +162 -0
- package/dist/core/apply.js +816 -0
- package/dist/core/auth-detection.d.ts +35 -0
- package/dist/core/auth-detection.js +358 -0
- package/dist/core/auto-extract.d.ts +82 -0
- package/dist/core/auto-extract.js +604 -0
- package/dist/core/auto-interact.d.ts +23 -0
- package/dist/core/auto-interact.js +246 -0
- package/dist/core/bm25-filter.d.ts +66 -0
- package/dist/core/bm25-filter.js +288 -0
- package/dist/core/branding.d.ts +54 -0
- package/dist/core/branding.js +234 -0
- package/dist/core/browser-fetch.d.ts +323 -0
- package/dist/core/browser-fetch.js +1600 -0
- package/dist/core/browser-pool.d.ts +91 -0
- package/dist/core/browser-pool.js +550 -0
- package/dist/core/budget.d.ts +42 -0
- package/dist/core/budget.js +324 -0
- package/dist/core/business-intel.d.ts +47 -0
- package/dist/core/business-intel.js +279 -0
- package/dist/core/cache.d.ts +13 -0
- package/dist/core/cache.js +121 -0
- package/dist/core/cf-worker-proxy.d.ts +32 -0
- package/dist/core/cf-worker-proxy.js +87 -0
- package/dist/core/challenge-detection.d.ts +26 -0
- package/dist/core/challenge-detection.js +468 -0
- package/dist/core/change-tracking.d.ts +75 -0
- package/dist/core/change-tracking.js +276 -0
- package/dist/core/chunker.d.ts +46 -0
- package/dist/core/chunker.js +249 -0
- package/dist/core/chunking.d.ts +42 -0
- package/dist/core/chunking.js +181 -0
- package/dist/core/circuit-breaker.d.ts +44 -0
- package/dist/core/circuit-breaker.js +85 -0
- package/dist/core/content-pruner.d.ts +47 -0
- package/dist/core/content-pruner.js +425 -0
- package/dist/core/cookie-cache.d.ts +60 -0
- package/dist/core/cookie-cache.js +163 -0
- package/dist/core/crawl-checkpoint.d.ts +54 -0
- package/dist/core/crawl-checkpoint.js +104 -0
- package/dist/core/crawler.d.ts +84 -0
- package/dist/core/crawler.js +349 -0
- package/dist/core/cross-verify.d.ts +27 -0
- package/dist/core/cross-verify.js +93 -0
- package/dist/core/deep-fetch.d.ts +74 -0
- package/dist/core/deep-fetch.js +405 -0
- package/dist/core/deep-research.d.ts +141 -0
- package/dist/core/deep-research.js +972 -0
- package/dist/core/design-analysis.d.ts +70 -0
- package/dist/core/design-analysis.js +490 -0
- package/dist/core/design-compare.d.ts +38 -0
- package/dist/core/design-compare.js +264 -0
- package/dist/core/diff.d.ts +61 -0
- package/dist/core/diff.js +289 -0
- package/dist/core/dns-cache.d.ts +20 -0
- package/dist/core/dns-cache.js +198 -0
- package/dist/core/documents.d.ts +23 -0
- package/dist/core/documents.js +123 -0
- package/dist/core/domain-memory.d.ts +66 -0
- package/dist/core/domain-memory.js +163 -0
- package/dist/core/domain-verify.d.ts +40 -0
- package/dist/core/domain-verify.js +379 -0
- package/dist/core/engine-ranker.d.ts +112 -0
- package/dist/core/engine-ranker.js +395 -0
- package/dist/core/extract-inline.d.ts +38 -0
- package/dist/core/extract-inline.js +215 -0
- package/dist/core/extract-listings.d.ts +38 -0
- package/dist/core/extract-listings.js +461 -0
- package/dist/core/extract.d.ts +9 -0
- package/dist/core/extract.js +139 -0
- package/dist/core/fetch-cache.d.ts +57 -0
- package/dist/core/fetch-cache.js +95 -0
- package/dist/core/fetcher.d.ts +13 -0
- package/dist/core/fetcher.js +12 -0
- package/dist/core/google-cache.d.ts +29 -0
- package/dist/core/google-cache.js +180 -0
- package/dist/core/google-serp-parser.d.ts +82 -0
- package/dist/core/google-serp-parser.js +287 -0
- package/dist/core/hotel-search.d.ts +122 -0
- package/dist/core/hotel-search.js +382 -0
- package/dist/core/http-fetch.d.ts +72 -0
- package/dist/core/http-fetch.js +820 -0
- package/dist/core/human.d.ts +175 -0
- package/dist/core/human.js +680 -0
- package/dist/core/image-caption.d.ts +44 -0
- package/dist/core/image-caption.js +271 -0
- package/dist/core/jobs.d.ts +75 -0
- package/dist/core/jobs.js +634 -0
- package/dist/core/json-ld.d.ts +15 -0
- package/dist/core/json-ld.js +617 -0
- package/dist/core/language-detect.d.ts +18 -0
- package/dist/core/language-detect.js +135 -0
- package/dist/core/links.d.ts +10 -0
- package/dist/core/links.js +44 -0
- package/dist/core/llm-extract.d.ts +71 -0
- package/dist/core/llm-extract.js +507 -0
- package/dist/core/llm-provider.d.ts +100 -0
- package/dist/core/llm-provider.js +702 -0
- package/dist/core/local-search.d.ts +60 -0
- package/dist/core/local-search.js +308 -0
- package/dist/core/logger.d.ts +28 -0
- package/dist/core/logger.js +104 -0
- package/dist/core/map.d.ts +33 -0
- package/dist/core/map.js +127 -0
- package/dist/core/markdown.d.ts +92 -0
- package/dist/core/markdown.js +809 -0
- package/dist/core/metadata.d.ts +34 -0
- package/dist/core/metadata.js +422 -0
- package/dist/core/observe.d.ts +113 -0
- package/dist/core/observe.js +395 -0
- package/dist/core/ocr.d.ts +12 -0
- package/dist/core/ocr.js +33 -0
- package/dist/core/paginate.d.ts +31 -0
- package/dist/core/paginate.js +106 -0
- package/dist/core/pdf.d.ts +8 -0
- package/dist/core/pdf.js +25 -0
- package/dist/core/peel-tls.d.ts +25 -0
- package/dist/core/peel-tls.js +220 -0
- package/dist/core/pipeline.d.ts +132 -0
- package/dist/core/pipeline.js +1666 -0
- package/dist/core/profiles.d.ts +61 -0
- package/dist/core/profiles.js +350 -0
- package/dist/core/prompt-guard.d.ts +30 -0
- package/dist/core/prompt-guard.js +119 -0
- package/dist/core/proxy-config.d.ts +90 -0
- package/dist/core/proxy-config.js +172 -0
- package/dist/core/quick-answer.d.ts +53 -0
- package/dist/core/quick-answer.js +833 -0
- package/dist/core/rate-governor.d.ts +80 -0
- package/dist/core/rate-governor.js +238 -0
- package/dist/core/readability.d.ts +57 -0
- package/dist/core/readability.js +533 -0
- package/dist/core/research.d.ts +66 -0
- package/dist/core/research.js +270 -0
- package/dist/core/retry.d.ts +60 -0
- package/dist/core/retry.js +119 -0
- package/dist/core/safe-browsing.d.ts +30 -0
- package/dist/core/safe-browsing.js +206 -0
- package/dist/core/schema-extraction.d.ts +66 -0
- package/dist/core/schema-extraction.js +352 -0
- package/dist/core/schema-postprocess.d.ts +32 -0
- package/dist/core/schema-postprocess.js +469 -0
- package/dist/core/schema-templates.d.ts +19 -0
- package/dist/core/schema-templates.js +143 -0
- package/dist/core/screenshot.d.ts +224 -0
- package/dist/core/screenshot.js +207 -0
- package/dist/core/search-engines.d.ts +25 -0
- package/dist/core/search-engines.js +182 -0
- package/dist/core/search-provider.d.ts +243 -0
- package/dist/core/search-provider.js +1629 -0
- package/dist/core/searxng-provider.d.ts +35 -0
- package/dist/core/searxng-provider.js +105 -0
- package/dist/core/selective-evidence.d.ts +151 -0
- package/dist/core/selective-evidence.js +389 -0
- package/dist/core/site-search.d.ts +44 -0
- package/dist/core/site-search.js +252 -0
- package/dist/core/sitemap.d.ts +23 -0
- package/dist/core/sitemap.js +105 -0
- package/dist/core/source-credibility.d.ts +29 -0
- package/dist/core/source-credibility.js +584 -0
- package/dist/core/source-scoring.d.ts +166 -0
- package/dist/core/source-scoring.js +396 -0
- package/dist/core/stemmer.d.ts +38 -0
- package/dist/core/stemmer.js +509 -0
- package/dist/core/strategies.d.ts +104 -0
- package/dist/core/strategies.js +1044 -0
- package/dist/core/strategy-hooks.d.ts +145 -0
- package/dist/core/strategy-hooks.js +74 -0
- package/dist/core/structured-extract.d.ts +43 -0
- package/dist/core/structured-extract.js +550 -0
- package/dist/core/summarize.d.ts +17 -0
- package/dist/core/summarize.js +78 -0
- package/dist/core/synonyms.d.ts +42 -0
- package/dist/core/synonyms.js +184 -0
- package/dist/core/system-monitor.d.ts +61 -0
- package/dist/core/system-monitor.js +133 -0
- package/dist/core/table-format.d.ts +30 -0
- package/dist/core/table-format.js +146 -0
- package/dist/core/threat-feeds.d.ts +23 -0
- package/dist/core/threat-feeds.js +104 -0
- package/dist/core/timing.d.ts +21 -0
- package/dist/core/timing.js +33 -0
- package/dist/core/transcript-export.d.ts +47 -0
- package/dist/core/transcript-export.js +107 -0
- package/dist/core/user-agents.d.ts +82 -0
- package/dist/core/user-agents.js +239 -0
- package/dist/core/vertical-search.d.ts +54 -0
- package/dist/core/vertical-search.js +158 -0
- package/dist/core/watch-manager.d.ts +175 -0
- package/dist/core/watch-manager.js +416 -0
- package/dist/core/watch.d.ts +101 -0
- package/dist/core/watch.js +389 -0
- package/dist/core/youtube.d.ts +130 -0
- package/dist/core/youtube.js +1175 -0
- package/dist/ee/challenge-re-export.d.ts +1 -0
- package/dist/ee/challenge-re-export.js +1 -0
- package/dist/ee/challenge-solver.d.ts +72 -0
- package/dist/ee/challenge-solver.js +720 -0
- package/dist/ee/domain-extractors.d.ts +8 -0
- package/dist/ee/domain-extractors.js +8 -0
- package/dist/ee/domain-intel.d.ts +16 -0
- package/dist/ee/domain-intel.js +133 -0
- package/dist/ee/extractors/allrecipes.d.ts +2 -0
- package/dist/ee/extractors/allrecipes.js +120 -0
- package/dist/ee/extractors/amazon.d.ts +2 -0
- package/dist/ee/extractors/amazon.js +78 -0
- package/dist/ee/extractors/arxiv.d.ts +2 -0
- package/dist/ee/extractors/arxiv.js +137 -0
- package/dist/ee/extractors/bestbuy.d.ts +2 -0
- package/dist/ee/extractors/bestbuy.js +78 -0
- package/dist/ee/extractors/carscom.d.ts +2 -0
- package/dist/ee/extractors/carscom.js +121 -0
- package/dist/ee/extractors/coingecko.d.ts +2 -0
- package/dist/ee/extractors/coingecko.js +134 -0
- package/dist/ee/extractors/craigslist.d.ts +2 -0
- package/dist/ee/extractors/craigslist.js +92 -0
- package/dist/ee/extractors/devto.d.ts +2 -0
- package/dist/ee/extractors/devto.js +135 -0
- package/dist/ee/extractors/ebay.d.ts +2 -0
- package/dist/ee/extractors/ebay.js +90 -0
- package/dist/ee/extractors/espn.d.ts +2 -0
- package/dist/ee/extractors/espn.js +260 -0
- package/dist/ee/extractors/etsy.d.ts +2 -0
- package/dist/ee/extractors/etsy.js +52 -0
- package/dist/ee/extractors/facebook.d.ts +2 -0
- package/dist/ee/extractors/facebook.js +46 -0
- package/dist/ee/extractors/github.d.ts +2 -0
- package/dist/ee/extractors/github.js +196 -0
- package/dist/ee/extractors/google-flights.d.ts +2 -0
- package/dist/ee/extractors/google-flights.js +176 -0
- package/dist/ee/extractors/hackernews.d.ts +2 -0
- package/dist/ee/extractors/hackernews.js +147 -0
- package/dist/ee/extractors/imdb.d.ts +2 -0
- package/dist/ee/extractors/imdb.js +172 -0
- package/dist/ee/extractors/index.d.ts +26 -0
- package/dist/ee/extractors/index.js +247 -0
- package/dist/ee/extractors/instagram.d.ts +2 -0
- package/dist/ee/extractors/instagram.js +102 -0
- package/dist/ee/extractors/kalshi.d.ts +2 -0
- package/dist/ee/extractors/kalshi.js +121 -0
- package/dist/ee/extractors/kayak-cars.d.ts +2 -0
- package/dist/ee/extractors/kayak-cars.js +270 -0
- package/dist/ee/extractors/linkedin.d.ts +2 -0
- package/dist/ee/extractors/linkedin.js +113 -0
- package/dist/ee/extractors/medium.d.ts +2 -0
- package/dist/ee/extractors/medium.js +130 -0
- package/dist/ee/extractors/news.d.ts +4 -0
- package/dist/ee/extractors/news.js +173 -0
- package/dist/ee/extractors/npm.d.ts +2 -0
- package/dist/ee/extractors/npm.js +86 -0
- package/dist/ee/extractors/pdf.d.ts +2 -0
- package/dist/ee/extractors/pdf.js +108 -0
- package/dist/ee/extractors/pinterest.d.ts +2 -0
- package/dist/ee/extractors/pinterest.js +34 -0
- package/dist/ee/extractors/polymarket.d.ts +2 -0
- package/dist/ee/extractors/polymarket.js +358 -0
- package/dist/ee/extractors/producthunt.d.ts +2 -0
- package/dist/ee/extractors/producthunt.js +88 -0
- package/dist/ee/extractors/pubmed.d.ts +2 -0
- package/dist/ee/extractors/pubmed.js +162 -0
- package/dist/ee/extractors/pypi.d.ts +2 -0
- package/dist/ee/extractors/pypi.js +80 -0
- package/dist/ee/extractors/reddit.d.ts +2 -0
- package/dist/ee/extractors/reddit.js +438 -0
- package/dist/ee/extractors/redfin.d.ts +2 -0
- package/dist/ee/extractors/redfin.js +156 -0
- package/dist/ee/extractors/semanticscholar.d.ts +2 -0
- package/dist/ee/extractors/semanticscholar.js +131 -0
- package/dist/ee/extractors/shared.d.ts +12 -0
- package/dist/ee/extractors/shared.js +76 -0
- package/dist/ee/extractors/soundcloud.d.ts +2 -0
- package/dist/ee/extractors/soundcloud.js +34 -0
- package/dist/ee/extractors/sportsbetting.d.ts +2 -0
- package/dist/ee/extractors/sportsbetting.js +37 -0
- package/dist/ee/extractors/spotify.d.ts +2 -0
- package/dist/ee/extractors/spotify.js +34 -0
- package/dist/ee/extractors/stackoverflow.d.ts +2 -0
- package/dist/ee/extractors/stackoverflow.js +61 -0
- package/dist/ee/extractors/substack.d.ts +2 -0
- package/dist/ee/extractors/substack.js +115 -0
- package/dist/ee/extractors/substackroot.d.ts +2 -0
- package/dist/ee/extractors/substackroot.js +46 -0
- package/dist/ee/extractors/tiktok.d.ts +2 -0
- package/dist/ee/extractors/tiktok.js +29 -0
- package/dist/ee/extractors/tradingview.d.ts +2 -0
- package/dist/ee/extractors/tradingview.js +182 -0
- package/dist/ee/extractors/twitch.d.ts +2 -0
- package/dist/ee/extractors/twitch.js +36 -0
- package/dist/ee/extractors/twitter.d.ts +2 -0
- package/dist/ee/extractors/twitter.js +327 -0
- package/dist/ee/extractors/types.d.ts +14 -0
- package/dist/ee/extractors/types.js +1 -0
- package/dist/ee/extractors/walmart.d.ts +2 -0
- package/dist/ee/extractors/walmart.js +50 -0
- package/dist/ee/extractors/weather.d.ts +2 -0
- package/dist/ee/extractors/weather.js +133 -0
- package/dist/ee/extractors/wikipedia.d.ts +4 -0
- package/dist/ee/extractors/wikipedia.js +235 -0
- package/dist/ee/extractors/yelp.d.ts +2 -0
- package/dist/ee/extractors/yelp.js +216 -0
- package/dist/ee/extractors/youtube.d.ts +2 -0
- package/dist/ee/extractors/youtube.js +189 -0
- package/dist/ee/extractors/zillow.d.ts +54 -0
- package/dist/ee/extractors/zillow.js +247 -0
- package/dist/ee/extractors-re-export.d.ts +1 -0
- package/dist/ee/extractors-re-export.js +1 -0
- package/dist/ee/premium-hooks.d.ts +20 -0
- package/dist/ee/premium-hooks.js +50 -0
- package/dist/ee/spa-detection.d.ts +2 -0
- package/dist/ee/spa-detection.js +2 -0
- package/dist/ee/stability.d.ts +4 -0
- package/dist/ee/stability.js +29 -0
- package/dist/ee/swr-cache.d.ts +14 -0
- package/dist/ee/swr-cache.js +34 -0
- package/dist/index.d.ts +143 -0
- package/dist/index.js +291 -0
- package/dist/integrations/index.d.ts +2 -0
- package/dist/integrations/index.js +2 -0
- package/dist/integrations/langchain.d.ts +64 -0
- package/dist/integrations/langchain.js +115 -0
- package/dist/integrations/llamaindex.d.ts +50 -0
- package/dist/integrations/llamaindex.js +91 -0
- package/dist/mcp/handlers/act.d.ts +5 -0
- package/dist/mcp/handlers/act.js +34 -0
- package/dist/mcp/handlers/definitions.d.ts +6 -0
- package/dist/mcp/handlers/definitions.js +395 -0
- package/dist/mcp/handlers/extract.d.ts +7 -0
- package/dist/mcp/handlers/extract.js +135 -0
- package/dist/mcp/handlers/fetch.d.ts +6 -0
- package/dist/mcp/handlers/fetch.js +98 -0
- package/dist/mcp/handlers/find.d.ts +5 -0
- package/dist/mcp/handlers/find.js +137 -0
- package/dist/mcp/handlers/index.d.ts +13 -0
- package/dist/mcp/handlers/index.js +63 -0
- package/dist/mcp/handlers/legacy.d.ts +25 -0
- package/dist/mcp/handlers/legacy.js +450 -0
- package/dist/mcp/handlers/meta.d.ts +6 -0
- package/dist/mcp/handlers/meta.js +40 -0
- package/dist/mcp/handlers/monitor.d.ts +5 -0
- package/dist/mcp/handlers/monitor.js +41 -0
- package/dist/mcp/handlers/observe.d.ts +8 -0
- package/dist/mcp/handlers/observe.js +37 -0
- package/dist/mcp/handlers/read.d.ts +6 -0
- package/dist/mcp/handlers/read.js +78 -0
- package/dist/mcp/handlers/see.d.ts +5 -0
- package/dist/mcp/handlers/see.js +75 -0
- package/dist/mcp/handlers/types.d.ts +29 -0
- package/dist/mcp/handlers/types.js +28 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +108 -0
- package/dist/mcp/smart-router.d.ts +23 -0
- package/dist/mcp/smart-router.js +178 -0
- package/dist/server/app.d.ts +14 -0
- package/dist/server/app.js +632 -0
- package/dist/server/auth-store.d.ts +28 -0
- package/dist/server/auth-store.js +88 -0
- package/dist/server/bull-queues.d.ts +60 -0
- package/dist/server/bull-queues.js +90 -0
- package/dist/server/email-service.d.ts +55 -0
- package/dist/server/email-service.js +291 -0
- package/dist/server/job-queue.d.ts +100 -0
- package/dist/server/job-queue.js +145 -0
- package/dist/server/logger.d.ts +10 -0
- package/dist/server/logger.js +37 -0
- package/dist/server/middleware/audit-log.d.ts +14 -0
- package/dist/server/middleware/audit-log.js +73 -0
- package/dist/server/middleware/auth.d.ts +35 -0
- package/dist/server/middleware/auth.js +225 -0
- package/dist/server/middleware/rate-limit.d.ts +50 -0
- package/dist/server/middleware/rate-limit.js +270 -0
- package/dist/server/middleware/scope-guard.d.ts +25 -0
- package/dist/server/middleware/scope-guard.js +45 -0
- package/dist/server/middleware/url-validator.d.ts +15 -0
- package/dist/server/middleware/url-validator.js +201 -0
- package/dist/server/openapi.yaml +6418 -0
- package/dist/server/pg-auth-store.d.ts +146 -0
- package/dist/server/pg-auth-store.js +576 -0
- package/dist/server/pg-job-queue.d.ts +59 -0
- package/dist/server/pg-job-queue.js +375 -0
- package/dist/server/routes/activity.d.ts +6 -0
- package/dist/server/routes/activity.js +79 -0
- package/dist/server/routes/admin-active.d.ts +7 -0
- package/dist/server/routes/admin-active.js +120 -0
- package/dist/server/routes/admin-stats.d.ts +7 -0
- package/dist/server/routes/admin-stats.js +176 -0
- package/dist/server/routes/agent.d.ts +24 -0
- package/dist/server/routes/agent.js +480 -0
- package/dist/server/routes/answer.d.ts +5 -0
- package/dist/server/routes/answer.js +125 -0
- package/dist/server/routes/ask.d.ts +28 -0
- package/dist/server/routes/ask.js +295 -0
- package/dist/server/routes/batch.d.ts +6 -0
- package/dist/server/routes/batch.js +493 -0
- package/dist/server/routes/cache-warm.d.ts +25 -0
- package/dist/server/routes/cache-warm.js +212 -0
- package/dist/server/routes/cli-usage.d.ts +6 -0
- package/dist/server/routes/cli-usage.js +127 -0
- package/dist/server/routes/compat.d.ts +23 -0
- package/dist/server/routes/compat.js +652 -0
- package/dist/server/routes/crawl.d.ts +13 -0
- package/dist/server/routes/crawl.js +287 -0
- package/dist/server/routes/deep-fetch.d.ts +8 -0
- package/dist/server/routes/deep-fetch.js +57 -0
- package/dist/server/routes/deep-research.d.ts +11 -0
- package/dist/server/routes/deep-research.js +232 -0
- package/dist/server/routes/demo.d.ts +24 -0
- package/dist/server/routes/demo.js +517 -0
- package/dist/server/routes/do.d.ts +8 -0
- package/dist/server/routes/do.js +72 -0
- package/dist/server/routes/extract.d.ts +14 -0
- package/dist/server/routes/extract.js +325 -0
- package/dist/server/routes/feed.d.ts +15 -0
- package/dist/server/routes/feed.js +311 -0
- package/dist/server/routes/fetch-queue.d.ts +13 -0
- package/dist/server/routes/fetch-queue.js +357 -0
- package/dist/server/routes/fetch.d.ts +7 -0
- package/dist/server/routes/fetch.js +1274 -0
- package/dist/server/routes/go.d.ts +14 -0
- package/dist/server/routes/go.js +81 -0
- package/dist/server/routes/health.d.ts +11 -0
- package/dist/server/routes/health.js +141 -0
- package/dist/server/routes/jobs.d.ts +7 -0
- package/dist/server/routes/jobs.js +574 -0
- package/dist/server/routes/map.d.ts +11 -0
- package/dist/server/routes/map.js +116 -0
- package/dist/server/routes/mcp.d.ts +14 -0
- package/dist/server/routes/mcp.js +197 -0
- package/dist/server/routes/metrics.d.ts +37 -0
- package/dist/server/routes/metrics.js +149 -0
- package/dist/server/routes/oauth.d.ts +9 -0
- package/dist/server/routes/oauth.js +396 -0
- package/dist/server/routes/playground.d.ts +17 -0
- package/dist/server/routes/playground.js +283 -0
- package/dist/server/routes/reader.d.ts +18 -0
- package/dist/server/routes/reader.js +192 -0
- package/dist/server/routes/research.d.ts +14 -0
- package/dist/server/routes/research.js +482 -0
- package/dist/server/routes/screenshot.d.ts +22 -0
- package/dist/server/routes/screenshot.js +820 -0
- package/dist/server/routes/search.d.ts +6 -0
- package/dist/server/routes/search.js +874 -0
- package/dist/server/routes/session.d.ts +17 -0
- package/dist/server/routes/session.js +548 -0
- package/dist/server/routes/share.d.ts +18 -0
- package/dist/server/routes/share.js +462 -0
- package/dist/server/routes/smart-search/handlers/cars.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/cars.js +102 -0
- package/dist/server/routes/smart-search/handlers/flights.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/flights.js +72 -0
- package/dist/server/routes/smart-search/handlers/general.d.ts +13 -0
- package/dist/server/routes/smart-search/handlers/general.js +717 -0
- package/dist/server/routes/smart-search/handlers/hotels.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/hotels.js +88 -0
- package/dist/server/routes/smart-search/handlers/products.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/products.js +1309 -0
- package/dist/server/routes/smart-search/handlers/rental.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/rental.js +154 -0
- package/dist/server/routes/smart-search/handlers/restaurants.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/restaurants.js +225 -0
- package/dist/server/routes/smart-search/handlers/transit-verdict.d.ts +41 -0
- package/dist/server/routes/smart-search/handlers/transit-verdict.js +224 -0
- package/dist/server/routes/smart-search/index.d.ts +19 -0
- package/dist/server/routes/smart-search/index.js +546 -0
- package/dist/server/routes/smart-search/intent.d.ts +3 -0
- package/dist/server/routes/smart-search/intent.js +264 -0
- package/dist/server/routes/smart-search/llm.d.ts +16 -0
- package/dist/server/routes/smart-search/llm.js +70 -0
- package/dist/server/routes/smart-search/sources/reddit.d.ts +18 -0
- package/dist/server/routes/smart-search/sources/reddit.js +34 -0
- package/dist/server/routes/smart-search/sources/yelp.d.ts +25 -0
- package/dist/server/routes/smart-search/sources/yelp.js +171 -0
- package/dist/server/routes/smart-search/sources/youtube.d.ts +8 -0
- package/dist/server/routes/smart-search/sources/youtube.js +9 -0
- package/dist/server/routes/smart-search/types.d.ts +81 -0
- package/dist/server/routes/smart-search/types.js +1 -0
- package/dist/server/routes/smart-search/utils.d.ts +20 -0
- package/dist/server/routes/smart-search/utils.js +146 -0
- package/dist/server/routes/stats.d.ts +6 -0
- package/dist/server/routes/stats.js +71 -0
- package/dist/server/routes/stripe.d.ts +15 -0
- package/dist/server/routes/stripe.js +296 -0
- package/dist/server/routes/transcript-export.d.ts +10 -0
- package/dist/server/routes/transcript-export.js +178 -0
- package/dist/server/routes/usage.d.ts +9 -0
- package/dist/server/routes/usage.js +279 -0
- package/dist/server/routes/users.d.ts +8 -0
- package/dist/server/routes/users.js +1867 -0
- package/dist/server/routes/watch.d.ts +15 -0
- package/dist/server/routes/watch.js +309 -0
- package/dist/server/routes/webhooks.d.ts +26 -0
- package/dist/server/routes/webhooks.js +170 -0
- package/dist/server/routes/youtube.d.ts +6 -0
- package/dist/server/routes/youtube.js +130 -0
- package/dist/server/sentry.d.ts +14 -0
- package/dist/server/sentry.js +104 -0
- package/dist/server/types.d.ts +15 -0
- package/dist/server/types.js +7 -0
- package/dist/server/utils/response.d.ts +44 -0
- package/dist/server/utils/response.js +69 -0
- package/dist/server/utils/sse.d.ts +22 -0
- package/dist/server/utils/sse.js +38 -0
- package/dist/types.d.ts +552 -0
- package/dist/types.js +39 -0
- package/llms.txt +105 -0
- package/package.json +189 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Session API — stateful Playwright sessions
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/session → create session, returns { sessionId, expiresAt }
|
|
5
|
+
* GET /v1/session/:id → get current page content (Readability text)
|
|
6
|
+
* POST /v1/session/:id/navigate → navigate to URL { url }
|
|
7
|
+
* POST /v1/session/:id/act → execute PageActions array
|
|
8
|
+
* GET /v1/session/:id/screenshot → take screenshot (image/png)
|
|
9
|
+
* GET /v1/session/:id/cookies → export cookies from session context
|
|
10
|
+
* POST /v1/session/:id/cookies → inject cookies into session context
|
|
11
|
+
* DELETE /v1/session/:id → close session
|
|
12
|
+
*
|
|
13
|
+
* Use cases: login flows, multi-step automation, UI testing, cookie persistence.
|
|
14
|
+
* This is what Browserbase charges $500/mo for — built into WebPeel.
|
|
15
|
+
*/
|
|
16
|
+
import { Router } from 'express';
|
|
17
|
+
export declare function createSessionRouter(): Router;
|
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Session API — stateful Playwright sessions
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/session → create session, returns { sessionId, expiresAt }
|
|
5
|
+
* GET /v1/session/:id → get current page content (Readability text)
|
|
6
|
+
* POST /v1/session/:id/navigate → navigate to URL { url }
|
|
7
|
+
* POST /v1/session/:id/act → execute PageActions array
|
|
8
|
+
* GET /v1/session/:id/screenshot → take screenshot (image/png)
|
|
9
|
+
* GET /v1/session/:id/cookies → export cookies from session context
|
|
10
|
+
* POST /v1/session/:id/cookies → inject cookies into session context
|
|
11
|
+
* DELETE /v1/session/:id → close session
|
|
12
|
+
*
|
|
13
|
+
* Use cases: login flows, multi-step automation, UI testing, cookie persistence.
|
|
14
|
+
* This is what Browserbase charges $500/mo for — built into WebPeel.
|
|
15
|
+
*/
|
|
16
|
+
import { Router } from 'express';
|
|
17
|
+
import { randomUUID } from 'crypto';
|
|
18
|
+
import { normalizeActions, executeActions } from '../../core/actions.js';
|
|
19
|
+
import { ANTI_DETECTION_ARGS, getRandomViewport, getRandomUserAgent, applyStealthScripts, } from '../../core/browser-pool.js';
|
|
20
|
+
import { extractReadableContent } from '../../core/readability.js';
|
|
21
|
+
const sessions = new Map();
|
|
22
|
+
const DEFAULT_SESSION_TTL_MS = 5 * 60 * 1000; // 5 minutes idle TTL (default)
|
|
23
|
+
const MAX_SESSION_TTL_MS = 60 * 60 * 1000; // 60 minutes (persist / max)
|
|
24
|
+
const MIN_SESSION_TTL_MS = 1 * 60 * 1000; // 1 minute minimum
|
|
25
|
+
const MAX_SESSIONS_PER_USER = 3; // prevent abuse
|
|
26
|
+
// Cleanup expired sessions every minute
|
|
27
|
+
const _cleanupInterval = setInterval(() => {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
for (const [id, session] of sessions) {
|
|
30
|
+
if (now - session.lastUsedAt > session.ttlMs) {
|
|
31
|
+
session.browser.close().catch(() => { });
|
|
32
|
+
sessions.delete(id);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}, 60_000);
|
|
36
|
+
// Don't keep the Node process alive just for the cleanup timer
|
|
37
|
+
if (_cleanupInterval.unref)
|
|
38
|
+
_cleanupInterval.unref();
|
|
39
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
40
|
+
/** Extract the owner ID from the request — supports both API key and JWT auth. */
|
|
41
|
+
function getOwnerId(req) {
|
|
42
|
+
return req.auth?.keyInfo?.accountId
|
|
43
|
+
|| req.user?.userId
|
|
44
|
+
|| null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Look up a session by id and verify it belongs to the requesting owner.
|
|
48
|
+
* Returns null if not found, expired, or owned by someone else.
|
|
49
|
+
*/
|
|
50
|
+
function getSession(id, ownerId) {
|
|
51
|
+
const session = sessions.get(id);
|
|
52
|
+
if (!session)
|
|
53
|
+
return null;
|
|
54
|
+
if (ownerId && session.ownerId !== ownerId)
|
|
55
|
+
return null; // ownership check
|
|
56
|
+
return session;
|
|
57
|
+
}
|
|
58
|
+
/** Launch a fresh Chromium browser for a session (separate instance per session). */
|
|
59
|
+
async function launchBrowser() {
|
|
60
|
+
const { chromium } = await import('playwright');
|
|
61
|
+
const vp = getRandomViewport();
|
|
62
|
+
return chromium.launch({
|
|
63
|
+
headless: true,
|
|
64
|
+
args: [...ANTI_DETECTION_ARGS, `--window-size=${vp.width},${vp.height}`],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Extract readable text from an HTML string using WebPeel's built-in Readability engine. */
|
|
68
|
+
function extractReadableText(html, url) {
|
|
69
|
+
try {
|
|
70
|
+
const result = extractReadableContent(html, url);
|
|
71
|
+
return result.content?.trim() || result.excerpt?.trim() || '';
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ── Router ────────────────────────────────────────────────────────────────────
|
|
78
|
+
export function createSessionRouter() {
|
|
79
|
+
const router = Router();
|
|
80
|
+
/**
|
|
81
|
+
* POST /v1/session — create a stateful browser session
|
|
82
|
+
*
|
|
83
|
+
* Body params:
|
|
84
|
+
* url? {string} Initial URL to navigate to (optional).
|
|
85
|
+
* ttl? {number} Session idle TTL in minutes (1–60, default 5).
|
|
86
|
+
* Timer resets on every request that touches the session.
|
|
87
|
+
* persist? {boolean} Shorthand for ttl=60. Enables long-lived sessions
|
|
88
|
+
* for login flows where cookies must persist.
|
|
89
|
+
*
|
|
90
|
+
* Returns: { sessionId, currentUrl, expiresAt, ttlMinutes }
|
|
91
|
+
*/
|
|
92
|
+
router.post('/v1/session', async (req, res) => {
|
|
93
|
+
const ownerId = getOwnerId(req);
|
|
94
|
+
if (!ownerId) {
|
|
95
|
+
res.status(401).json({ success: false, error: { type: 'auth_required', message: 'Valid API key or session required.', docs: 'https://webpeel.dev/docs/authentication' }, requestId: req.requestId });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Enforce per-user session cap
|
|
99
|
+
const userSessions = [...sessions.values()].filter(s => s.ownerId === ownerId);
|
|
100
|
+
if (userSessions.length >= MAX_SESSIONS_PER_USER) {
|
|
101
|
+
res.status(429).json({
|
|
102
|
+
success: false,
|
|
103
|
+
error: {
|
|
104
|
+
type: 'session_limit',
|
|
105
|
+
message: `Maximum ${MAX_SESSIONS_PER_USER} concurrent sessions per user. Delete an existing session first.`,
|
|
106
|
+
hint: 'Delete an existing session via DELETE /v1/session/:id before creating a new one.',
|
|
107
|
+
docs: 'https://webpeel.dev/docs/errors#session-limit',
|
|
108
|
+
},
|
|
109
|
+
requestId: req.requestId || randomUUID(),
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const { url, ttl, persist } = req.body;
|
|
114
|
+
// Resolve TTL: persist=true → 60 min max, ttl overrides default, clamp to [1, 60] min
|
|
115
|
+
let ttlMs = DEFAULT_SESSION_TTL_MS;
|
|
116
|
+
if (persist) {
|
|
117
|
+
ttlMs = MAX_SESSION_TTL_MS;
|
|
118
|
+
}
|
|
119
|
+
else if (typeof ttl === 'number') {
|
|
120
|
+
ttlMs = Math.min(MAX_SESSION_TTL_MS, Math.max(MIN_SESSION_TTL_MS, ttl * 60 * 1000));
|
|
121
|
+
}
|
|
122
|
+
let browser = null;
|
|
123
|
+
try {
|
|
124
|
+
browser = await launchBrowser();
|
|
125
|
+
const context = await browser.newContext({
|
|
126
|
+
userAgent: getRandomUserAgent(),
|
|
127
|
+
viewport: { width: 1280, height: 800 },
|
|
128
|
+
});
|
|
129
|
+
const page = await context.newPage();
|
|
130
|
+
await applyStealthScripts(page);
|
|
131
|
+
if (url) {
|
|
132
|
+
try {
|
|
133
|
+
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
|
|
134
|
+
}
|
|
135
|
+
catch (navErr) {
|
|
136
|
+
// Navigation failed — still return the session, caller can retry
|
|
137
|
+
const errMsg = navErr instanceof Error ? navErr.message : String(navErr);
|
|
138
|
+
await browser.close().catch(() => { });
|
|
139
|
+
res.status(502).json({
|
|
140
|
+
success: false,
|
|
141
|
+
error: {
|
|
142
|
+
type: 'navigation_failed',
|
|
143
|
+
message: errMsg,
|
|
144
|
+
hint: 'Check that the URL is accessible and try again.',
|
|
145
|
+
docs: 'https://webpeel.dev/docs/errors#navigation-failed',
|
|
146
|
+
},
|
|
147
|
+
requestId: req.requestId || randomUUID(),
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const id = randomUUID();
|
|
153
|
+
const now = Date.now();
|
|
154
|
+
sessions.set(id, {
|
|
155
|
+
id,
|
|
156
|
+
browser,
|
|
157
|
+
context,
|
|
158
|
+
page,
|
|
159
|
+
ownerId,
|
|
160
|
+
createdAt: now,
|
|
161
|
+
lastUsedAt: now,
|
|
162
|
+
currentUrl: page.url(),
|
|
163
|
+
ttlMs,
|
|
164
|
+
});
|
|
165
|
+
res.status(201).json({
|
|
166
|
+
sessionId: id,
|
|
167
|
+
currentUrl: page.url(),
|
|
168
|
+
expiresAt: new Date(now + ttlMs).toISOString(),
|
|
169
|
+
ttlMinutes: ttlMs / 60_000,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
if (browser)
|
|
174
|
+
await browser.close().catch(() => { });
|
|
175
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
176
|
+
res.status(500).json({
|
|
177
|
+
success: false,
|
|
178
|
+
error: {
|
|
179
|
+
type: 'session_create_failed',
|
|
180
|
+
message: msg,
|
|
181
|
+
docs: 'https://webpeel.dev/docs/errors#session-create-failed',
|
|
182
|
+
},
|
|
183
|
+
requestId: req.requestId || randomUUID(),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// ── GET /v1/session/:id — get page content ───────────────────────────────────
|
|
188
|
+
router.get('/v1/session/:id', async (req, res) => {
|
|
189
|
+
const ownerId = getOwnerId(req);
|
|
190
|
+
const session = getSession(req.params['id'], ownerId);
|
|
191
|
+
if (!session) {
|
|
192
|
+
res.status(404).json({
|
|
193
|
+
success: false,
|
|
194
|
+
error: {
|
|
195
|
+
type: 'session_not_found',
|
|
196
|
+
message: 'Session not found or has expired.',
|
|
197
|
+
hint: 'Create a new session via POST /v1/session.',
|
|
198
|
+
docs: 'https://webpeel.dev/docs/errors#session-not-found',
|
|
199
|
+
},
|
|
200
|
+
requestId: req.requestId || randomUUID(),
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const [html, title] = await Promise.all([
|
|
206
|
+
session.page.content(),
|
|
207
|
+
session.page.title(),
|
|
208
|
+
]);
|
|
209
|
+
const content = await extractReadableText(html, session.page.url());
|
|
210
|
+
session.lastUsedAt = Date.now();
|
|
211
|
+
res.json({
|
|
212
|
+
sessionId: session.id,
|
|
213
|
+
currentUrl: session.page.url(),
|
|
214
|
+
title,
|
|
215
|
+
content,
|
|
216
|
+
expiresAt: new Date(session.lastUsedAt + session.ttlMs).toISOString(),
|
|
217
|
+
ttlMinutes: session.ttlMs / 60_000,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
222
|
+
res.status(500).json({
|
|
223
|
+
success: false,
|
|
224
|
+
error: {
|
|
225
|
+
type: 'session_error',
|
|
226
|
+
message: msg,
|
|
227
|
+
docs: 'https://webpeel.dev/docs/errors#session-error',
|
|
228
|
+
},
|
|
229
|
+
requestId: req.requestId || randomUUID(),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
// ── POST /v1/session/:id/navigate ────────────────────────────────────────────
|
|
234
|
+
router.post('/v1/session/:id/navigate', async (req, res) => {
|
|
235
|
+
const ownerId = getOwnerId(req);
|
|
236
|
+
const session = getSession(req.params["id"], ownerId);
|
|
237
|
+
if (!session) {
|
|
238
|
+
res.status(404).json({
|
|
239
|
+
success: false,
|
|
240
|
+
error: {
|
|
241
|
+
type: 'session_not_found',
|
|
242
|
+
message: 'Session not found or has expired.',
|
|
243
|
+
hint: 'Create a new session via POST /v1/session.',
|
|
244
|
+
docs: 'https://webpeel.dev/docs/errors#session-not-found',
|
|
245
|
+
},
|
|
246
|
+
requestId: req.requestId || randomUUID(),
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const { url } = req.body;
|
|
251
|
+
if (!url) {
|
|
252
|
+
res.status(400).json({
|
|
253
|
+
success: false,
|
|
254
|
+
error: {
|
|
255
|
+
type: 'bad_request',
|
|
256
|
+
message: '`url` is required.',
|
|
257
|
+
hint: 'Pass a URL in the request body: { "url": "https://example.com" }',
|
|
258
|
+
docs: 'https://webpeel.dev/docs/errors#bad-request',
|
|
259
|
+
},
|
|
260
|
+
requestId: req.requestId || randomUUID(),
|
|
261
|
+
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
await session.page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
|
|
266
|
+
session.lastUsedAt = Date.now();
|
|
267
|
+
session.currentUrl = session.page.url();
|
|
268
|
+
res.json({
|
|
269
|
+
currentUrl: session.page.url(),
|
|
270
|
+
title: await session.page.title(),
|
|
271
|
+
expiresAt: new Date(session.lastUsedAt + session.ttlMs).toISOString(),
|
|
272
|
+
ttlMinutes: session.ttlMs / 60_000,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
277
|
+
res.status(502).json({
|
|
278
|
+
success: false,
|
|
279
|
+
error: {
|
|
280
|
+
type: 'navigation_failed',
|
|
281
|
+
message: msg,
|
|
282
|
+
hint: 'Check that the URL is accessible and try again.',
|
|
283
|
+
docs: 'https://webpeel.dev/docs/errors#navigation-failed',
|
|
284
|
+
},
|
|
285
|
+
requestId: req.requestId || randomUUID(),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
// ── POST /v1/session/:id/act — execute actions ───────────────────────────────
|
|
290
|
+
router.post('/v1/session/:id/act', async (req, res) => {
|
|
291
|
+
const ownerId = getOwnerId(req);
|
|
292
|
+
const session = getSession(req.params["id"], ownerId);
|
|
293
|
+
if (!session) {
|
|
294
|
+
res.status(404).json({
|
|
295
|
+
success: false,
|
|
296
|
+
error: {
|
|
297
|
+
type: 'session_not_found',
|
|
298
|
+
message: 'Session not found or has expired.',
|
|
299
|
+
hint: 'Create a new session via POST /v1/session.',
|
|
300
|
+
docs: 'https://webpeel.dev/docs/errors#session-not-found',
|
|
301
|
+
},
|
|
302
|
+
requestId: req.requestId || randomUUID(),
|
|
303
|
+
});
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const { actions, screenshot: takeScreenshot } = req.body;
|
|
307
|
+
let normalized;
|
|
308
|
+
try {
|
|
309
|
+
normalized = normalizeActions(actions);
|
|
310
|
+
}
|
|
311
|
+
catch (err) {
|
|
312
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
313
|
+
res.status(400).json({
|
|
314
|
+
success: false,
|
|
315
|
+
error: {
|
|
316
|
+
type: 'invalid_actions',
|
|
317
|
+
message: msg,
|
|
318
|
+
hint: 'Pass a valid actions array: [{ "type": "click", "selector": "#btn" }]',
|
|
319
|
+
docs: 'https://webpeel.dev/docs/errors#invalid-actions',
|
|
320
|
+
},
|
|
321
|
+
requestId: req.requestId || randomUUID(),
|
|
322
|
+
});
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (!normalized?.length) {
|
|
326
|
+
res.status(400).json({
|
|
327
|
+
success: false,
|
|
328
|
+
error: {
|
|
329
|
+
type: 'bad_request',
|
|
330
|
+
message: '`actions` must be a non-empty array.',
|
|
331
|
+
hint: 'Pass a valid actions array: [{ "type": "click", "selector": "#btn" }]',
|
|
332
|
+
docs: 'https://webpeel.dev/docs/errors#bad-request',
|
|
333
|
+
},
|
|
334
|
+
requestId: req.requestId || randomUUID(),
|
|
335
|
+
});
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const normalizedActions = normalized;
|
|
339
|
+
try {
|
|
340
|
+
await executeActions(session.page, normalizedActions);
|
|
341
|
+
session.lastUsedAt = Date.now();
|
|
342
|
+
session.currentUrl = session.page.url();
|
|
343
|
+
let screenshot;
|
|
344
|
+
if (takeScreenshot) {
|
|
345
|
+
const buf = await session.page.screenshot({ type: 'png' });
|
|
346
|
+
screenshot = buf.toString('base64');
|
|
347
|
+
}
|
|
348
|
+
const [title, currentUrl] = await Promise.all([
|
|
349
|
+
session.page.title(),
|
|
350
|
+
Promise.resolve(session.page.url()),
|
|
351
|
+
]);
|
|
352
|
+
res.json({
|
|
353
|
+
currentUrl,
|
|
354
|
+
title,
|
|
355
|
+
screenshot,
|
|
356
|
+
actionsExecuted: normalizedActions.length,
|
|
357
|
+
expiresAt: new Date(session.lastUsedAt + session.ttlMs).toISOString(),
|
|
358
|
+
ttlMinutes: session.ttlMs / 60_000,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
363
|
+
res.status(502).json({
|
|
364
|
+
success: false,
|
|
365
|
+
error: {
|
|
366
|
+
type: 'action_failed',
|
|
367
|
+
message: msg,
|
|
368
|
+
hint: 'Check your action selectors and ensure the page is loaded.',
|
|
369
|
+
docs: 'https://webpeel.dev/docs/errors#action-failed',
|
|
370
|
+
},
|
|
371
|
+
requestId: req.requestId || randomUUID(),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
// ── GET /v1/session/:id/screenshot ───────────────────────────────────────────
|
|
376
|
+
router.get('/v1/session/:id/screenshot', async (req, res) => {
|
|
377
|
+
const ownerId = getOwnerId(req);
|
|
378
|
+
const session = getSession(req.params["id"], ownerId);
|
|
379
|
+
if (!session) {
|
|
380
|
+
res.status(404).json({
|
|
381
|
+
success: false,
|
|
382
|
+
error: {
|
|
383
|
+
type: 'session_not_found',
|
|
384
|
+
message: 'Session not found or has expired.',
|
|
385
|
+
hint: 'Create a new session via POST /v1/session.',
|
|
386
|
+
docs: 'https://webpeel.dev/docs/errors#session-not-found',
|
|
387
|
+
},
|
|
388
|
+
requestId: req.requestId || randomUUID(),
|
|
389
|
+
});
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
const fullPage = req.query.fullPage === 'true';
|
|
394
|
+
const buf = await session.page.screenshot({ type: 'png', fullPage });
|
|
395
|
+
session.lastUsedAt = Date.now();
|
|
396
|
+
res.setHeader('Content-Type', 'image/png');
|
|
397
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
398
|
+
res.setHeader('X-Session-Expires-At', new Date(session.lastUsedAt + session.ttlMs).toISOString());
|
|
399
|
+
res.send(buf);
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
403
|
+
res.status(500).json({
|
|
404
|
+
success: false,
|
|
405
|
+
error: {
|
|
406
|
+
type: 'screenshot_failed',
|
|
407
|
+
message: msg,
|
|
408
|
+
docs: 'https://webpeel.dev/docs/errors#screenshot-failed',
|
|
409
|
+
},
|
|
410
|
+
requestId: req.requestId || randomUUID(),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
/**
|
|
415
|
+
* GET /v1/session/:id/cookies — export all cookies from the session's browser context
|
|
416
|
+
*
|
|
417
|
+
* Returns: { sessionId, cookies: Cookie[], count: number, expiresAt: string }
|
|
418
|
+
*
|
|
419
|
+
* Each cookie follows the Playwright Cookie shape:
|
|
420
|
+
* { name, value, domain, path, expires, httpOnly, secure, sameSite }
|
|
421
|
+
*
|
|
422
|
+
* Use this to snapshot cookies after a login flow, then re-inject them later
|
|
423
|
+
* via POST /v1/session/:id/cookies to skip re-authentication.
|
|
424
|
+
*/
|
|
425
|
+
router.get('/v1/session/:id/cookies', async (req, res) => {
|
|
426
|
+
const ownerId = getOwnerId(req);
|
|
427
|
+
const session = getSession(req.params['id'], ownerId);
|
|
428
|
+
if (!session) {
|
|
429
|
+
res.status(404).json({
|
|
430
|
+
success: false,
|
|
431
|
+
error: {
|
|
432
|
+
type: 'session_not_found',
|
|
433
|
+
message: 'Session not found or has expired.',
|
|
434
|
+
hint: 'Create a new session via POST /v1/session.',
|
|
435
|
+
docs: 'https://webpeel.dev/docs/errors#session-not-found',
|
|
436
|
+
},
|
|
437
|
+
requestId: req.requestId || randomUUID(),
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
// Playwright context.cookies() returns all cookies for all URLs by default
|
|
443
|
+
const cookies = await session.context.cookies();
|
|
444
|
+
session.lastUsedAt = Date.now();
|
|
445
|
+
res.json({
|
|
446
|
+
sessionId: session.id,
|
|
447
|
+
cookies,
|
|
448
|
+
count: cookies.length,
|
|
449
|
+
expiresAt: new Date(session.lastUsedAt + session.ttlMs).toISOString(),
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
catch (err) {
|
|
453
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
454
|
+
res.status(500).json({
|
|
455
|
+
success: false,
|
|
456
|
+
error: {
|
|
457
|
+
type: 'cookie_export_failed',
|
|
458
|
+
message: msg,
|
|
459
|
+
docs: 'https://webpeel.dev/docs/errors#cookie-export-failed',
|
|
460
|
+
},
|
|
461
|
+
requestId: req.requestId || randomUUID(),
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
/**
|
|
466
|
+
* POST /v1/session/:id/cookies — inject cookies into the session's browser context
|
|
467
|
+
*
|
|
468
|
+
* Body params:
|
|
469
|
+
* cookies {Cookie[]} Array of Playwright-compatible cookie objects.
|
|
470
|
+
* Required fields: name, value, domain (or url).
|
|
471
|
+
* Optional: path, expires, httpOnly, secure, sameSite.
|
|
472
|
+
*
|
|
473
|
+
* Returns: { sessionId, injected: number, expiresAt: string }
|
|
474
|
+
*
|
|
475
|
+
* Typical cookie-persistence workflow:
|
|
476
|
+
* 1. POST /v1/session { url: "https://example.com", persist: true }
|
|
477
|
+
* 2. POST /v1/session/:id/act (complete login flow)
|
|
478
|
+
* 3. GET /v1/session/:id/cookies → save cookies array to your storage
|
|
479
|
+
* 4. Later: POST /v1/session/:id/cookies { cookies: [...] }
|
|
480
|
+
* 5. GET /v1/session/:id → page loads authenticated (no re-login needed)
|
|
481
|
+
*/
|
|
482
|
+
router.post('/v1/session/:id/cookies', async (req, res) => {
|
|
483
|
+
const ownerId = getOwnerId(req);
|
|
484
|
+
const session = getSession(req.params['id'], ownerId);
|
|
485
|
+
if (!session) {
|
|
486
|
+
res.status(404).json({
|
|
487
|
+
success: false,
|
|
488
|
+
error: {
|
|
489
|
+
type: 'session_not_found',
|
|
490
|
+
message: 'Session not found or has expired.',
|
|
491
|
+
hint: 'Create a new session via POST /v1/session.',
|
|
492
|
+
docs: 'https://webpeel.dev/docs/errors#session-not-found',
|
|
493
|
+
},
|
|
494
|
+
requestId: req.requestId || randomUUID(),
|
|
495
|
+
});
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const { cookies } = req.body;
|
|
499
|
+
if (!Array.isArray(cookies) || cookies.length === 0) {
|
|
500
|
+
res.status(400).json({
|
|
501
|
+
success: false,
|
|
502
|
+
error: {
|
|
503
|
+
type: 'bad_request',
|
|
504
|
+
message: '`cookies` must be a non-empty array of cookie objects.',
|
|
505
|
+
hint: 'Pass cookies exported from GET /v1/session/:id/cookies or a compatible Cookie[] array.',
|
|
506
|
+
docs: 'https://webpeel.dev/docs/errors#bad-request',
|
|
507
|
+
},
|
|
508
|
+
requestId: req.requestId || randomUUID(),
|
|
509
|
+
});
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
// Playwright's addCookies validates the shape internally; invalid cookies will throw
|
|
514
|
+
await session.context.addCookies(cookies);
|
|
515
|
+
session.lastUsedAt = Date.now();
|
|
516
|
+
res.json({
|
|
517
|
+
sessionId: session.id,
|
|
518
|
+
injected: cookies.length,
|
|
519
|
+
expiresAt: new Date(session.lastUsedAt + session.ttlMs).toISOString(),
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
524
|
+
res.status(400).json({
|
|
525
|
+
success: false,
|
|
526
|
+
error: {
|
|
527
|
+
type: 'cookie_inject_failed',
|
|
528
|
+
message: msg,
|
|
529
|
+
hint: 'Ensure each cookie has at minimum: name, value, and domain (or url).',
|
|
530
|
+
docs: 'https://webpeel.dev/docs/errors#cookie-inject-failed',
|
|
531
|
+
},
|
|
532
|
+
requestId: req.requestId || randomUUID(),
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
// ── DELETE /v1/session/:id ───────────────────────────────────────────────────
|
|
537
|
+
router.delete('/v1/session/:id', async (req, res) => {
|
|
538
|
+
const ownerId = getOwnerId(req);
|
|
539
|
+
const session = getSession(req.params["id"], ownerId);
|
|
540
|
+
if (session) {
|
|
541
|
+
sessions.delete(req.params["id"]);
|
|
542
|
+
await session.browser.close().catch(() => { });
|
|
543
|
+
}
|
|
544
|
+
// Always return 200 (idempotent delete)
|
|
545
|
+
res.json({ closed: true });
|
|
546
|
+
});
|
|
547
|
+
return router;
|
|
548
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shareable read links — short public URLs for fetched content
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/share — create a short link (auth required, 50/day limit)
|
|
5
|
+
* GET /s/:id — serve shared content (public, no auth)
|
|
6
|
+
*
|
|
7
|
+
* IDs are 9-char base64url strings (crypto.randomBytes(6).toString('base64url').slice(0, 9))
|
|
8
|
+
* Shares expire after 30 days. view_count is incremented on every public read.
|
|
9
|
+
*/
|
|
10
|
+
import { Router } from 'express';
|
|
11
|
+
import pg from 'pg';
|
|
12
|
+
/** Generate a cryptographically secure 9-char base64url ID.
|
|
13
|
+
* randomBytes(7) → base64url gives 10 chars (7*4/3=9.33→10), slice to 9.
|
|
14
|
+
* Note: randomBytes(6) → base64url gives only 8 chars (6/3*4=8), so we need 7+ bytes.
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateShareId(): string;
|
|
17
|
+
export declare function createSharePublicRouter(pool: pg.Pool | null): Router;
|
|
18
|
+
export declare function createShareRouter(pool: pg.Pool | null): Router;
|