@taodigital/crawler 0.0.0-stage → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +82 -0
- package/README.md +10 -2
- package/dist/accessibility-auditor.d.ts +29 -0
- package/dist/accessibility-auditor.js +57 -0
- package/dist/accessibility-auditor.js.map +7 -0
- package/dist/accessibility-tree-crawler.d.ts +44 -0
- package/dist/accessibility-tree-crawler.js +156 -0
- package/dist/accessibility-tree-crawler.js.map +7 -0
- package/dist/advanced-ui-probes.d.ts +47 -0
- package/dist/advanced-ui-probes.js +310 -0
- package/dist/advanced-ui-probes.js.map +7 -0
- package/dist/ai-login.d.ts +32 -0
- package/dist/ai-login.js +250 -0
- package/dist/ai-login.js.map +7 -0
- package/dist/auth-token-rotator.d.ts +47 -0
- package/dist/auth-token-rotator.js +58 -0
- package/dist/auth-token-rotator.js.map +7 -0
- package/dist/auth.d.ts +20 -0
- package/dist/auth.js +348 -0
- package/dist/auth.js.map +7 -0
- package/dist/browser-extension.d.ts +37 -0
- package/dist/browser-extension.js +113 -0
- package/dist/browser-extension.js.map +7 -0
- package/dist/captcha-solver.d.ts +16 -0
- package/dist/captcha-solver.js +105 -0
- package/dist/captcha-solver.js.map +7 -0
- package/dist/color-contrast.d.ts +30 -0
- package/dist/color-contrast.js +78 -0
- package/dist/color-contrast.js.map +7 -0
- package/dist/content-chunker.d.ts +18 -0
- package/dist/content-chunker.js +152 -0
- package/dist/content-chunker.js.map +7 -0
- package/dist/content-dedup.d.ts +23 -0
- package/dist/content-dedup.js +60 -0
- package/dist/content-dedup.js.map +7 -0
- package/dist/cookie-consent.d.ts +25 -0
- package/dist/cookie-consent.js +78 -0
- package/dist/cookie-consent.js.map +7 -0
- package/dist/cookie-jar.d.ts +20 -0
- package/dist/cookie-jar.js +61 -0
- package/dist/cookie-jar.js.map +7 -0
- package/dist/crawl-session-manager.d.ts +44 -0
- package/dist/crawl-session-manager.js +145 -0
- package/dist/crawl-session-manager.js.map +7 -0
- package/dist/crawler.d.ts +233 -0
- package/dist/crawler.js +6239 -0
- package/dist/crawler.js.map +7 -0
- package/dist/crud-interaction.d.ts +31 -0
- package/dist/crud-interaction.js +264 -0
- package/dist/crud-interaction.js.map +7 -0
- package/dist/custom-js-injector.d.ts +42 -0
- package/dist/custom-js-injector.js +50 -0
- package/dist/custom-js-injector.js.map +7 -0
- package/dist/dark-mode-capture.d.ts +23 -0
- package/dist/dark-mode-capture.js +75 -0
- package/dist/dark-mode-capture.js.map +7 -0
- package/dist/dead-link-detector.d.ts +37 -0
- package/dist/dead-link-detector.js +50 -0
- package/dist/dead-link-detector.js.map +7 -0
- package/dist/dropdown-enumerator.d.ts +20 -0
- package/dist/dropdown-enumerator.js +230 -0
- package/dist/dropdown-enumerator.js.map +7 -0
- package/dist/element-hash.d.ts +9 -0
- package/dist/element-hash.js +9 -0
- package/dist/element-hash.js.map +7 -0
- package/dist/email-otp.d.ts +42 -0
- package/dist/email-otp.js +177 -0
- package/dist/email-otp.js.map +7 -0
- package/dist/engine-router.d.ts +47 -0
- package/dist/engine-router.js +94 -0
- package/dist/engine-router.js.map +7 -0
- package/dist/export/bulk-export.d.ts +26 -0
- package/dist/export/bulk-export.js +76 -0
- package/dist/export/bulk-export.js.map +7 -0
- package/dist/export/vector-export.d.ts +76 -0
- package/dist/export/vector-export.js +85 -0
- package/dist/export/vector-export.js.map +7 -0
- package/dist/extractors/advanced.d.ts +64 -0
- package/dist/extractors/advanced.js +265 -0
- package/dist/extractors/advanced.js.map +7 -0
- package/dist/extractors/artifact-envelope.d.ts +63 -0
- package/dist/extractors/artifact-envelope.js +63 -0
- package/dist/extractors/artifact-envelope.js.map +7 -0
- package/dist/extractors/branding-extractor.d.ts +46 -0
- package/dist/extractors/branding-extractor.js +104 -0
- package/dist/extractors/branding-extractor.js.map +7 -0
- package/dist/extractors/summary-extractor.d.ts +10 -0
- package/dist/extractors/summary-extractor.js +36 -0
- package/dist/extractors/summary-extractor.js.map +7 -0
- package/dist/failure-analysis-agent.d.ts +16 -0
- package/dist/failure-analysis-agent.js +42 -0
- package/dist/failure-analysis-agent.js.map +7 -0
- package/dist/feature-flag-detector.d.ts +30 -0
- package/dist/feature-flag-detector.js +55 -0
- package/dist/feature-flag-detector.js.map +7 -0
- package/dist/filter-pagination-crawler.d.ts +52 -0
- package/dist/filter-pagination-crawler.js +299 -0
- package/dist/filter-pagination-crawler.js.map +7 -0
- package/dist/form-detector.d.ts +34 -0
- package/dist/form-detector.js +58 -0
- package/dist/form-detector.js.map +7 -0
- package/dist/frontier.d.ts +40 -0
- package/dist/frontier.js +103 -0
- package/dist/frontier.js.map +7 -0
- package/dist/gap-pass.d.ts +36 -0
- package/dist/gap-pass.js +115 -0
- package/dist/gap-pass.js.map +7 -0
- package/dist/graphql-detector.d.ts +39 -0
- package/dist/graphql-detector.js +86 -0
- package/dist/graphql-detector.js.map +7 -0
- package/dist/http-engine.d.ts +80 -0
- package/dist/http-engine.js +318 -0
- package/dist/http-engine.js.map +7 -0
- package/dist/incremental-diff.d.ts +116 -0
- package/dist/incremental-diff.js +137 -0
- package/dist/incremental-diff.js.map +7 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +7 -0
- package/dist/input-fuzzer.d.ts +30 -0
- package/dist/input-fuzzer.js +105 -0
- package/dist/input-fuzzer.js.map +7 -0
- package/dist/interactive-probe.d.ts +144 -0
- package/dist/interactive-probe.js +865 -0
- package/dist/interactive-probe.js.map +7 -0
- package/dist/job-queue.d.ts +297 -0
- package/dist/job-queue.js +3812 -0
- package/dist/job-queue.js.map +7 -0
- package/dist/js-coverage.d.ts +29 -0
- package/dist/js-coverage.js +56 -0
- package/dist/js-coverage.js.map +7 -0
- package/dist/js-router-detector.d.ts +17 -0
- package/dist/js-router-detector.js +90 -0
- package/dist/js-router-detector.js.map +7 -0
- package/dist/language-detector.d.ts +22 -0
- package/dist/language-detector.js +57 -0
- package/dist/language-detector.js.map +7 -0
- package/dist/link-classifier.d.ts +23 -0
- package/dist/link-classifier.js +40 -0
- package/dist/link-classifier.js.map +7 -0
- package/dist/live-view.d.ts +36 -0
- package/dist/live-view.js +105 -0
- package/dist/live-view.js.map +7 -0
- package/dist/locator-extractor.d.ts +46 -0
- package/dist/locator-extractor.js +220 -0
- package/dist/locator-extractor.js.map +7 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +10 -0
- package/dist/logger.js.map +7 -0
- package/dist/markdown-extract.d.ts +8 -0
- package/dist/markdown-extract.js +54 -0
- package/dist/markdown-extract.js.map +7 -0
- package/dist/multi-context-handler.d.ts +55 -0
- package/dist/multi-context-handler.js +182 -0
- package/dist/multi-context-handler.js.map +7 -0
- package/dist/native-crawler.d.ts +69 -0
- package/dist/native-crawler.js +839 -0
- package/dist/native-crawler.js.map +7 -0
- package/dist/nested-window-crawler.d.ts +41 -0
- package/dist/nested-window-crawler.js +172 -0
- package/dist/nested-window-crawler.js.map +7 -0
- package/dist/network-action-correlator.d.ts +47 -0
- package/dist/network-action-correlator.js +209 -0
- package/dist/network-action-correlator.js.map +7 -0
- package/dist/network-observer.d.ts +49 -0
- package/dist/network-observer.js +149 -0
- package/dist/network-observer.js.map +7 -0
- package/dist/network-throttle.d.ts +25 -0
- package/dist/network-throttle.js +34 -0
- package/dist/network-throttle.js.map +7 -0
- package/dist/openapi-generator.d.ts +49 -0
- package/dist/openapi-generator.js +89 -0
- package/dist/openapi-generator.js.map +7 -0
- package/dist/pdf-extractor.d.ts +28 -0
- package/dist/pdf-extractor.js +81 -0
- package/dist/pdf-extractor.js.map +7 -0
- package/dist/performance-metrics-collector.d.ts +50 -0
- package/dist/performance-metrics-collector.js +155 -0
- package/dist/performance-metrics-collector.js.map +7 -0
- package/dist/performance-metrics.d.ts +52 -0
- package/dist/performance-metrics.js +83 -0
- package/dist/performance-metrics.js.map +7 -0
- package/dist/phash.d.ts +50 -0
- package/dist/phash.js +136 -0
- package/dist/phash.js.map +7 -0
- package/dist/pii-scrubber.d.ts +21 -0
- package/dist/pii-scrubber.js +75 -0
- package/dist/pii-scrubber.js.map +7 -0
- package/dist/playwright-recorder.d.ts +39 -0
- package/dist/playwright-recorder.js +123 -0
- package/dist/playwright-recorder.js.map +7 -0
- package/dist/policies/crawl-safety.d.ts +9 -0
- package/dist/policies/crawl-safety.js +41 -0
- package/dist/policies/crawl-safety.js.map +7 -0
- package/dist/profiles/domain-profile.d.ts +19 -0
- package/dist/profiles/domain-profile.js +160 -0
- package/dist/profiles/domain-profile.js.map +7 -0
- package/dist/proxy.d.ts +50 -0
- package/dist/proxy.js +165 -0
- package/dist/proxy.js.map +7 -0
- package/dist/rate-limiter.d.ts +31 -0
- package/dist/rate-limiter.js +64 -0
- package/dist/rate-limiter.js.map +7 -0
- package/dist/redirect-tracker.d.ts +37 -0
- package/dist/redirect-tracker.js +65 -0
- package/dist/redirect-tracker.js.map +7 -0
- package/dist/redis-frontier.d.ts +48 -0
- package/dist/redis-frontier.js +126 -0
- package/dist/redis-frontier.js.map +7 -0
- package/dist/redis-queue.d.ts +48 -0
- package/dist/redis-queue.js +165 -0
- package/dist/redis-queue.js.map +7 -0
- package/dist/response-cache.d.ts +38 -0
- package/dist/response-cache.js +140 -0
- package/dist/response-cache.js.map +7 -0
- package/dist/responsive-detector.d.ts +29 -0
- package/dist/responsive-detector.js +46 -0
- package/dist/responsive-detector.js.map +7 -0
- package/dist/retry-policy.d.ts +20 -0
- package/dist/retry-policy.js +54 -0
- package/dist/retry-policy.js.map +7 -0
- package/dist/robots-txt.d.ts +9 -0
- package/dist/robots-txt.js +77 -0
- package/dist/robots-txt.js.map +7 -0
- package/dist/screen-reader.d.ts +15 -0
- package/dist/screen-reader.js +48 -0
- package/dist/screen-reader.js.map +7 -0
- package/dist/screenshot-masker.d.ts +28 -0
- package/dist/screenshot-masker.js +62 -0
- package/dist/screenshot-masker.js.map +7 -0
- package/dist/seo-checker.d.ts +33 -0
- package/dist/seo-checker.js +91 -0
- package/dist/seo-checker.js.map +7 -0
- package/dist/service-worker-interceptor.d.ts +36 -0
- package/dist/service-worker-interceptor.js +136 -0
- package/dist/service-worker-interceptor.js.map +7 -0
- package/dist/session-capture.d.ts +39 -0
- package/dist/session-capture.js +274 -0
- package/dist/session-capture.js.map +7 -0
- package/dist/session-export.d.ts +49 -0
- package/dist/session-export.js +117 -0
- package/dist/session-export.js.map +7 -0
- package/dist/shadow-dom-crawler.d.ts +35 -0
- package/dist/shadow-dom-crawler.js +132 -0
- package/dist/shadow-dom-crawler.js.map +7 -0
- package/dist/sitemap-writer.d.ts +21 -0
- package/dist/sitemap-writer.js +47 -0
- package/dist/sitemap-writer.js.map +7 -0
- package/dist/sitemap.d.ts +51 -0
- package/dist/sitemap.js +160 -0
- package/dist/sitemap.js.map +7 -0
- package/dist/skyvern.d.ts +56 -0
- package/dist/skyvern.js +129 -0
- package/dist/skyvern.js.map +7 -0
- package/dist/sqs-queue.d.ts +13 -0
- package/dist/sqs-queue.js +115 -0
- package/dist/sqs-queue.js.map +7 -0
- package/dist/sso-token.d.ts +29 -0
- package/dist/sso-token.js +76 -0
- package/dist/sso-token.js.map +7 -0
- package/dist/state-graph.d.ts +59 -0
- package/dist/state-graph.js +193 -0
- package/dist/state-graph.js.map +7 -0
- package/dist/stealth/human-behavior.d.ts +20 -0
- package/dist/stealth/human-behavior.js +202 -0
- package/dist/stealth/human-behavior.js.map +7 -0
- package/dist/telemetry.d.ts +34 -0
- package/dist/telemetry.js +77 -0
- package/dist/telemetry.js.map +7 -0
- package/dist/test-executor.d.ts +81 -0
- package/dist/test-executor.js +443 -0
- package/dist/test-executor.js.map +7 -0
- package/dist/tls-fingerprint.d.ts +50 -0
- package/dist/tls-fingerprint.js +81 -0
- package/dist/tls-fingerprint.js.map +7 -0
- package/dist/url-pattern-completer.d.ts +28 -0
- package/dist/url-pattern-completer.js +122 -0
- package/dist/url-pattern-completer.js.map +7 -0
- package/dist/visual-diff-verdict.d.ts +40 -0
- package/dist/visual-diff-verdict.js +27 -0
- package/dist/visual-diff-verdict.js.map +7 -0
- package/dist/webhook-notifier.d.ts +30 -0
- package/dist/webhook-notifier.js +76 -0
- package/dist/webhook-notifier.js.map +7 -0
- package/dist/websocket-detector.d.ts +24 -0
- package/dist/websocket-detector.js +60 -0
- package/dist/websocket-detector.js.map +7 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +33 -0
- package/dist/worker.js.map +7 -0
- package/package.json +49 -4
- package/src/accessibility-auditor.ts +102 -0
- package/src/accessibility-tree-crawler.ts +246 -0
- package/src/advanced-ui-probes.ts +470 -0
- package/src/ai-login.ts +298 -0
- package/src/auth-token-rotator.ts +89 -0
- package/src/auth.ts +418 -0
- package/src/browser-extension.ts +186 -0
- package/src/captcha-solver.ts +143 -0
- package/src/color-contrast.ts +116 -0
- package/src/content-chunker.ts +170 -0
- package/src/content-dedup.ts +99 -0
- package/src/cookie-consent.ts +106 -0
- package/src/cookie-jar.ts +91 -0
- package/src/crawl-session-manager.ts +231 -0
- package/src/crawler.ts +7123 -0
- package/src/crawler.ts.rej +12 -0
- package/src/crud-interaction.ts +386 -0
- package/src/custom-js-injector.ts +93 -0
- package/src/dark-mode-capture.ts +110 -0
- package/src/dead-link-detector.ts +76 -0
- package/src/dropdown-enumerator.ts +312 -0
- package/src/element-hash.ts +16 -0
- package/src/email-otp.ts +252 -0
- package/src/engine-router.ts +141 -0
- package/src/export/bulk-export.ts +85 -0
- package/src/export/vector-export.ts +160 -0
- package/src/extractors/advanced.ts +309 -0
- package/src/extractors/artifact-envelope.ts +111 -0
- package/src/extractors/branding-extractor.ts +159 -0
- package/src/extractors/summary-extractor.ts +49 -0
- package/src/failure-analysis-agent.ts +51 -0
- package/src/feature-flag-detector.ts +97 -0
- package/src/filter-pagination-crawler.ts +500 -0
- package/src/form-detector.ts +103 -0
- package/src/frontier.test.ts +64 -0
- package/src/frontier.ts +144 -0
- package/src/gap-pass.ts +165 -0
- package/src/graphql-detector.ts +129 -0
- package/src/http-engine.ts +453 -0
- package/src/incremental-diff.ts +259 -0
- package/src/index.ts +52 -0
- package/src/input-fuzzer.ts +129 -0
- package/src/interactive-probe.ts +1136 -0
- package/src/job-queue.test.ts +187 -0
- package/src/job-queue.ts +4504 -0
- package/src/js-coverage.ts +87 -0
- package/src/js-router-detector.ts +112 -0
- package/src/language-detector.ts +82 -0
- package/src/link-classifier.ts +65 -0
- package/src/live-view.ts +146 -0
- package/src/locator-extractor.ts +309 -0
- package/src/logger.ts +9 -0
- package/src/markdown-extract.test.ts +73 -0
- package/src/markdown-extract.ts +76 -0
- package/src/multi-context-handler.ts +296 -0
- package/src/native-crawler.ts +1108 -0
- package/src/nested-window-crawler.ts +310 -0
- package/src/network-action-correlator.ts +320 -0
- package/src/network-observer.ts +204 -0
- package/src/network-throttle.ts +69 -0
- package/src/openapi-generator.ts +135 -0
- package/src/pdf-extractor.ts +123 -0
- package/src/performance-metrics-collector.ts +243 -0
- package/src/performance-metrics.ts +134 -0
- package/src/phash.ts +184 -0
- package/src/pii-scrubber.ts +99 -0
- package/src/playwright-recorder.ts +170 -0
- package/src/policies/crawl-safety.ts +52 -0
- package/src/profiles/domain-profile.ts +190 -0
- package/src/proxy.ts +238 -0
- package/src/rate-limiter.ts +87 -0
- package/src/redirect-tracker.ts +97 -0
- package/src/redis-frontier.ts +143 -0
- package/src/redis-queue.ts +208 -0
- package/src/response-cache.ts +150 -0
- package/src/responsive-detector.ts +92 -0
- package/src/retry-policy.ts +87 -0
- package/src/robots-txt.ts +96 -0
- package/src/screen-reader.ts +98 -0
- package/src/screenshot-masker.ts +98 -0
- package/src/seo-checker.ts +134 -0
- package/src/service-worker-interceptor.ts +185 -0
- package/src/session-capture.ts +335 -0
- package/src/session-export.test.ts +75 -0
- package/src/session-export.ts +172 -0
- package/src/shadow-dom-crawler.ts +198 -0
- package/src/sitemap-writer.ts +65 -0
- package/src/sitemap.test.ts +127 -0
- package/src/sitemap.ts +233 -0
- package/src/skyvern.ts +199 -0
- package/src/sqs-queue.ts +129 -0
- package/src/sso-token.ts +120 -0
- package/src/state-graph.ts +280 -0
- package/src/stealth/human-behavior.ts +227 -0
- package/src/telemetry.ts +122 -0
- package/src/test-executor.test.ts +72 -0
- package/src/test-executor.ts +578 -0
- package/src/tls-fingerprint.ts +111 -0
- package/src/url-pattern-completer.ts +161 -0
- package/src/vendor.d.ts +20 -0
- package/src/visual-diff-verdict.test.ts +80 -0
- package/src/visual-diff-verdict.ts +59 -0
- package/src/webhook-notifier.ts +109 -0
- package/src/websocket-detector.ts +83 -0
- package/src/worker.ts +50 -0
- package/tsconfig.json +13 -0
- package/types/accessibility-auditor.d.ts +29 -0
- package/types/accessibility-tree-crawler.d.ts +44 -0
- package/types/advanced-ui-probes.d.ts +47 -0
- package/types/ai-login.d.ts +32 -0
- package/types/auth-token-rotator.d.ts +47 -0
- package/types/auth.d.ts +20 -0
- package/types/browser-extension.d.ts +37 -0
- package/types/bull-worker.d.ts +6 -0
- package/types/captcha-solver.d.ts +16 -0
- package/types/color-contrast.d.ts +30 -0
- package/types/content-chunker.d.ts +18 -0
- package/types/content-dedup.d.ts +23 -0
- package/types/cookie-consent.d.ts +25 -0
- package/types/cookie-jar.d.ts +20 -0
- package/types/crawl-session-manager.d.ts +44 -0
- package/types/crawler.d.ts +233 -0
- package/types/crud-interaction.d.ts +31 -0
- package/types/custom-js-injector.d.ts +42 -0
- package/types/dark-mode-capture.d.ts +23 -0
- package/types/dead-link-detector.d.ts +37 -0
- package/types/dropdown-enumerator.d.ts +20 -0
- package/types/element-hash.d.ts +9 -0
- package/types/email-otp.d.ts +42 -0
- package/types/engine-router.d.ts +47 -0
- package/types/export/bulk-export.d.ts +26 -0
- package/types/export/vector-export.d.ts +76 -0
- package/types/extractors/advanced.d.ts +64 -0
- package/types/extractors/artifact-envelope.d.ts +63 -0
- package/types/extractors/branding-extractor.d.ts +46 -0
- package/types/extractors/summary-extractor.d.ts +10 -0
- package/types/failure-analysis-agent.d.ts +16 -0
- package/types/feature-flag-detector.d.ts +30 -0
- package/types/filter-pagination-crawler.d.ts +52 -0
- package/types/form-detector.d.ts +34 -0
- package/types/frontier.d.ts +40 -0
- package/types/gap-pass.d.ts +36 -0
- package/types/graphql-detector.d.ts +39 -0
- package/types/http-engine.d.ts +80 -0
- package/types/incremental-diff.d.ts +116 -0
- package/types/index.d.ts +52 -0
- package/types/input-fuzzer.d.ts +30 -0
- package/types/interactive-probe.d.ts +144 -0
- package/types/job-queue.d.ts +308 -0
- package/types/js-coverage.d.ts +29 -0
- package/types/js-router-detector.d.ts +17 -0
- package/types/language-detector.d.ts +22 -0
- package/types/link-classifier.d.ts +23 -0
- package/types/live-view.d.ts +36 -0
- package/types/locator-extractor.d.ts +46 -0
- package/types/logger.d.ts +2 -0
- package/types/markdown-extract.d.ts +8 -0
- package/types/multi-context-handler.d.ts +55 -0
- package/types/native-crawler.d.ts +69 -0
- package/types/nested-window-crawler.d.ts +41 -0
- package/types/network-action-correlator.d.ts +47 -0
- package/types/network-observer.d.ts +49 -0
- package/types/network-throttle.d.ts +25 -0
- package/types/openapi-generator.d.ts +49 -0
- package/types/pdf-extractor.d.ts +28 -0
- package/types/performance-metrics-collector.d.ts +50 -0
- package/types/performance-metrics.d.ts +52 -0
- package/types/phash.d.ts +50 -0
- package/types/pii-scrubber.d.ts +21 -0
- package/types/playwright-recorder.d.ts +39 -0
- package/types/policies/crawl-safety.d.ts +9 -0
- package/types/profiles/domain-profile.d.ts +19 -0
- package/types/proxy.d.ts +50 -0
- package/types/rate-limiter.d.ts +31 -0
- package/types/redirect-tracker.d.ts +37 -0
- package/types/redis-frontier.d.ts +48 -0
- package/types/redis-queue.d.ts +48 -0
- package/types/response-cache.d.ts +38 -0
- package/types/responsive-detector.d.ts +29 -0
- package/types/retry-policy.d.ts +20 -0
- package/types/robots-txt.d.ts +9 -0
- package/types/screen-reader.d.ts +15 -0
- package/types/screenshot-masker.d.ts +28 -0
- package/types/seo-checker.d.ts +33 -0
- package/types/service-worker-interceptor.d.ts +36 -0
- package/types/session-capture.d.ts +39 -0
- package/types/session-export.d.ts +49 -0
- package/types/shadow-dom-crawler.d.ts +35 -0
- package/types/sitemap-writer.d.ts +21 -0
- package/types/sitemap.d.ts +51 -0
- package/types/skyvern.d.ts +56 -0
- package/types/sqs-queue.d.ts +13 -0
- package/types/sso-token.d.ts +29 -0
- package/types/state-graph.d.ts +59 -0
- package/types/stealth/human-behavior.d.ts +20 -0
- package/types/telemetry.d.ts +34 -0
- package/types/test-executor.d.ts +84 -0
- package/types/tls-fingerprint.d.ts +50 -0
- package/types/url-pattern-completer.d.ts +28 -0
- package/types/visual-diff-verdict.d.ts +40 -0
- package/types/webhook-notifier.d.ts +30 -0
- package/types/websocket-detector.d.ts +24 -0
- package/types/worker.d.ts +1 -0
package/dist/skyvern.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
async function skyvernNavigate(startUrl, navigationGoals, tenantConfig, triggerReason) {
|
|
2
|
+
const apiUrl = tenantConfig?.apiUrl || process.env.SKYVERN_API_URL;
|
|
3
|
+
const apiKey = tenantConfig?.apiKey || process.env.SKYVERN_API_KEY;
|
|
4
|
+
if (!apiUrl || !apiKey) {
|
|
5
|
+
console.warn("[skyvern] skyvernNavigate: no API config \u2014 skipping post-login vision navigation");
|
|
6
|
+
return { screenshotUrls: [], visitedUrls: [], status: "failed" };
|
|
7
|
+
}
|
|
8
|
+
const visitedUrls = [];
|
|
9
|
+
const screenshotUrls = [];
|
|
10
|
+
const skyvernSteps = [];
|
|
11
|
+
let lastTaskId;
|
|
12
|
+
let totalDurationMs = 0;
|
|
13
|
+
for (const goal of navigationGoals) {
|
|
14
|
+
try {
|
|
15
|
+
const taskStart = Date.now();
|
|
16
|
+
const res = await fetch(`${apiUrl}/api/v1/tasks`, {
|
|
17
|
+
method: "POST",
|
|
18
|
+
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
19
|
+
body: JSON.stringify({
|
|
20
|
+
url: startUrl,
|
|
21
|
+
navigation_goal: goal,
|
|
22
|
+
...tenantConfig?.llmKey ? { llm_key: tenantConfig.llmKey } : process.env.SKYVERN_LLM_KEY ? { llm_key: process.env.SKYVERN_LLM_KEY } : {},
|
|
23
|
+
...tenantConfig?.llmApiKey ? { llm_api_key: tenantConfig.llmApiKey } : process.env.OPENAI_API_KEY ? { llm_api_key: process.env.OPENAI_API_KEY } : {}
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) continue;
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
const taskId = data.task_id;
|
|
29
|
+
if (!taskId) continue;
|
|
30
|
+
lastTaskId = taskId;
|
|
31
|
+
let done = false;
|
|
32
|
+
for (let p = 0; p < 12 && !done; p++) {
|
|
33
|
+
await new Promise((r) => setTimeout(r, 5e3));
|
|
34
|
+
const poll = await fetch(`${apiUrl}/api/v1/tasks/${taskId}`, {
|
|
35
|
+
headers: { "x-api-key": apiKey }
|
|
36
|
+
}).catch(() => null);
|
|
37
|
+
if (!poll?.ok) continue;
|
|
38
|
+
const pollData = await poll.json();
|
|
39
|
+
if (pollData.status === "completed" || pollData.status === "failed") {
|
|
40
|
+
done = true;
|
|
41
|
+
totalDurationMs += Date.now() - taskStart;
|
|
42
|
+
if (pollData.status === "completed") {
|
|
43
|
+
if (pollData.output?.url) visitedUrls.push(pollData.output.url);
|
|
44
|
+
if (pollData.output?.screenshot_url) screenshotUrls.push(pollData.output.screenshot_url);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const stepsRes = await fetch(`${apiUrl}/api/v1/tasks/${taskId}/steps`, {
|
|
48
|
+
headers: { "x-api-key": apiKey }
|
|
49
|
+
}).catch(() => null);
|
|
50
|
+
if (stepsRes?.ok) {
|
|
51
|
+
const stepsData = await stepsRes.json();
|
|
52
|
+
const stepsList = Array.isArray(stepsData) ? stepsData : stepsData?.steps ?? [];
|
|
53
|
+
for (const s of stepsList) {
|
|
54
|
+
if (s.output?.screenshot_url) screenshotUrls.push(s.output.screenshot_url);
|
|
55
|
+
skyvernSteps.push({
|
|
56
|
+
order: s.step_order ?? s.order ?? 0,
|
|
57
|
+
action_type: s.action_type ?? s.action ?? "unknown",
|
|
58
|
+
element_description: s.output?.element_description,
|
|
59
|
+
reasoning: s.output?.llm_response?.action_description ?? s.output?.reasoning,
|
|
60
|
+
success: s.status === "completed",
|
|
61
|
+
screenshot_url: s.output?.screenshot_url,
|
|
62
|
+
error: s.status === "failed" ? s.output?.errors?.[0] ?? "Step failed" : void 0
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.warn(`[skyvern] Navigation goal failed: ${goal}`, err);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
screenshotUrls,
|
|
76
|
+
visitedUrls,
|
|
77
|
+
status: visitedUrls.length > 0 ? "success" : screenshotUrls.length > 0 ? "partial" : "failed",
|
|
78
|
+
steps: skyvernSteps,
|
|
79
|
+
taskId: lastTaskId,
|
|
80
|
+
durationMs: totalDurationMs || void 0,
|
|
81
|
+
triggerReason
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async function skyvernFallback(task, tenantConfig) {
|
|
85
|
+
const apiUrl = tenantConfig?.apiUrl || process.env.SKYVERN_API_URL;
|
|
86
|
+
const apiKey = tenantConfig?.apiKey || process.env.SKYVERN_API_KEY;
|
|
87
|
+
if (!apiUrl || !apiKey) {
|
|
88
|
+
console.warn(
|
|
89
|
+
"[skyvern] Vision fallback disabled \u2014 set SKYVERN_API_URL + SKYVERN_API_KEY in .env to enable.",
|
|
90
|
+
"\n[skyvern] Self-host: https://github.com/Skyvern-AI/skyvern"
|
|
91
|
+
);
|
|
92
|
+
return "failed";
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const res = await fetch(`${apiUrl}/api/v1/tasks`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
98
|
+
body: JSON.stringify({
|
|
99
|
+
url: task.url,
|
|
100
|
+
navigation_goal: task.taskDescription,
|
|
101
|
+
...task.credentials && {
|
|
102
|
+
navigation_payload: {
|
|
103
|
+
username: task.credentials.username,
|
|
104
|
+
password: task.credentials.password,
|
|
105
|
+
...task.credentials.oneTimeCode && { otp: task.credentials.oneTimeCode }
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
...tenantConfig?.llmKey ? { llm_key: tenantConfig.llmKey } : process.env.SKYVERN_LLM_KEY ? { llm_key: process.env.SKYVERN_LLM_KEY } : {},
|
|
109
|
+
...tenantConfig?.llmApiKey ? { llm_api_key: tenantConfig.llmApiKey } : process.env.OPENAI_API_KEY ? { llm_api_key: process.env.OPENAI_API_KEY } : {}
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
if (!res.ok) {
|
|
113
|
+
const body = await res.text().catch(() => "");
|
|
114
|
+
console.error(`[skyvern] Task creation failed: ${res.status}`, body);
|
|
115
|
+
return "failed";
|
|
116
|
+
}
|
|
117
|
+
const data = await res.json();
|
|
118
|
+
console.log(`[skyvern] Task queued: ${data.task_id}. Poll GET /api/v1/tasks/${data.task_id} for completion.`);
|
|
119
|
+
return "success";
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.error("[skyvern] Error:", err);
|
|
122
|
+
return "failed";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
skyvernFallback,
|
|
127
|
+
skyvernNavigate
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=skyvern.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/skyvern.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Skyvern Vision Fallback\n *\n * Skyvern combines computer vision + LLM reasoning to navigate sites that\n * defeat DOM-based approaches: CAPTCHAs, legacy apps, visual-only login flows,\n * government portals. It's the \"break glass\" option when Stagehand fails.\n *\n * Production setup (Phase 4+):\n * 1. Self-host: https://github.com/Skyvern-AI/skyvern\n * 2. Or use cloud: https://app.skyvern.com\n * 3. Set SKYVERN_API_URL + SKYVERN_API_KEY in .env\n *\n * Pricing note: vision tasks cost more per step than DOM-based Stagehand.\n * Use only when the fast path fails.\n */\n\nexport interface SkyvernTask {\n taskDescription: string;\n url: string;\n credentials?: { username: string; password: string; oneTimeCode?: string };\n}\n\nexport interface SkyvernConfig {\n apiUrl: string;\n apiKey: string;\n llmKey?: string;\n llmApiKey?: string;\n}\n\nexport interface SkyvernStep {\n order: number;\n action_type: string;\n element_description?: string;\n reasoning?: string;\n success: boolean;\n screenshot_url?: string;\n duration_ms?: number;\n error?: string;\n}\n\nexport interface SkyvernNavigateResult {\n screenshotUrls: string[];\n visitedUrls: string[];\n status: 'success' | 'failed' | 'partial';\n steps?: SkyvernStep[];\n taskId?: string;\n durationMs?: number;\n triggerReason?: string;\n}\n\n/**\n * Gap 5: Post-login navigation via Skyvern vision for apps where DOM analysis\n * cannot discover screens (canvas, WebGL, legacy Flash-replacement stacks).\n * Skyvern drives the browser via screenshot + LLM, returning visited page list.\n */\nexport async function skyvernNavigate(\n startUrl: string,\n navigationGoals: string[],\n tenantConfig?: SkyvernConfig,\n triggerReason?: string,\n): Promise<SkyvernNavigateResult> {\n const apiUrl = tenantConfig?.apiUrl || process.env.SKYVERN_API_URL;\n const apiKey = tenantConfig?.apiKey || process.env.SKYVERN_API_KEY;\n\n if (!apiUrl || !apiKey) {\n console.warn('[skyvern] skyvernNavigate: no API config \u2014 skipping post-login vision navigation');\n return { screenshotUrls: [], visitedUrls: [], status: 'failed' };\n }\n\n const visitedUrls: string[] = [];\n const screenshotUrls: string[] = [];\n const skyvernSteps: SkyvernStep[] = [];\n let lastTaskId: string | undefined;\n let totalDurationMs = 0;\n\n for (const goal of navigationGoals) {\n try {\n const taskStart = Date.now();\n const res = await fetch(`${apiUrl}/api/v1/tasks`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },\n body: JSON.stringify({\n url: startUrl,\n navigation_goal: goal,\n ...(tenantConfig?.llmKey ? { llm_key: tenantConfig.llmKey } : process.env.SKYVERN_LLM_KEY ? { llm_key: process.env.SKYVERN_LLM_KEY } : {}),\n ...(tenantConfig?.llmApiKey ? { llm_api_key: tenantConfig.llmApiKey } : process.env.OPENAI_API_KEY ? { llm_api_key: process.env.OPENAI_API_KEY } : {}),\n }),\n });\n if (!res.ok) continue;\n const data: any = await res.json();\n const taskId: string = data.task_id;\n if (!taskId) continue;\n lastTaskId = taskId;\n\n // Poll for task completion (max 60s per goal)\n let done = false;\n for (let p = 0; p < 12 && !done; p++) {\n await new Promise((r) => setTimeout(r, 5000));\n const poll = await fetch(`${apiUrl}/api/v1/tasks/${taskId}`, {\n headers: { 'x-api-key': apiKey },\n }).catch(() => null);\n if (!poll?.ok) continue;\n const pollData: any = await poll.json();\n if (pollData.status === 'completed' || pollData.status === 'failed') {\n done = true;\n totalDurationMs += Date.now() - taskStart;\n if (pollData.status === 'completed') {\n if (pollData.output?.url) visitedUrls.push(pollData.output.url);\n if (pollData.output?.screenshot_url) screenshotUrls.push(pollData.output.screenshot_url);\n }\n\n // Fetch step-level detail from Skyvern\n try {\n const stepsRes = await fetch(`${apiUrl}/api/v1/tasks/${taskId}/steps`, {\n headers: { 'x-api-key': apiKey },\n }).catch(() => null);\n if (stepsRes?.ok) {\n const stepsData: any = await stepsRes.json();\n const stepsList = Array.isArray(stepsData) ? stepsData : (stepsData?.steps ?? []);\n for (const s of stepsList) {\n if (s.output?.screenshot_url) screenshotUrls.push(s.output.screenshot_url);\n skyvernSteps.push({\n order: s.step_order ?? s.order ?? 0,\n action_type: s.action_type ?? s.action ?? 'unknown',\n element_description: s.output?.element_description,\n reasoning: s.output?.llm_response?.action_description ?? s.output?.reasoning,\n success: s.status === 'completed',\n screenshot_url: s.output?.screenshot_url,\n error: s.status === 'failed' ? (s.output?.errors?.[0] ?? 'Step failed') : undefined,\n });\n }\n }\n } catch { /* non-fatal */ }\n }\n }\n } catch (err) {\n console.warn(`[skyvern] Navigation goal failed: ${goal}`, err);\n }\n }\n\n return {\n screenshotUrls,\n visitedUrls,\n status: visitedUrls.length > 0 ? 'success' : screenshotUrls.length > 0 ? 'partial' : 'failed',\n steps: skyvernSteps,\n taskId: lastTaskId,\n durationMs: totalDurationMs || undefined,\n triggerReason,\n };\n}\n\nexport async function skyvernFallback(task: SkyvernTask, tenantConfig?: SkyvernConfig): Promise<'success' | 'failed'> {\n const apiUrl = tenantConfig?.apiUrl || process.env.SKYVERN_API_URL;\n const apiKey = tenantConfig?.apiKey || process.env.SKYVERN_API_KEY;\n\n if (!apiUrl || !apiKey) {\n console.warn(\n '[skyvern] Vision fallback disabled \u2014 set SKYVERN_API_URL + SKYVERN_API_KEY in .env to enable.',\n '\\n[skyvern] Self-host: https://github.com/Skyvern-AI/skyvern'\n );\n return 'failed';\n }\n\n try {\n const res = await fetch(`${apiUrl}/api/v1/tasks`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },\n body: JSON.stringify({\n url: task.url,\n navigation_goal: task.taskDescription,\n ...(task.credentials && {\n navigation_payload: {\n username: task.credentials.username,\n password: task.credentials.password,\n ...(task.credentials.oneTimeCode && { otp: task.credentials.oneTimeCode }),\n },\n }),\n ...(tenantConfig?.llmKey ? { llm_key: tenantConfig.llmKey } : process.env.SKYVERN_LLM_KEY ? { llm_key: process.env.SKYVERN_LLM_KEY } : {}),\n ...(tenantConfig?.llmApiKey ? { llm_api_key: tenantConfig.llmApiKey } : process.env.OPENAI_API_KEY ? { llm_api_key: process.env.OPENAI_API_KEY } : {}),\n }),\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n console.error(`[skyvern] Task creation failed: ${res.status}`, body);\n return 'failed';\n }\n\n const data: any = await res.json();\n console.log(`[skyvern] Task queued: ${data.task_id}. Poll GET /api/v1/tasks/${data.task_id} for completion.`);\n // Full implementation: poll until status === \"completed\" | \"failed\".\n // For Phase 1 the login already happened in the browser context Skyvern manages,\n // so returning 'success' here means we can proceed in that browser session.\n return 'success';\n } catch (err) {\n console.error('[skyvern] Error:', err);\n return 'failed';\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function sendJobToSqs(jobId: string): Promise<void>;
|
|
2
|
+
/** Returns the approximate number of messages in the SQS DLQ. */
|
|
3
|
+
export declare function getDlqStats(): Promise<{
|
|
4
|
+
approximateMessageCount: number;
|
|
5
|
+
dlqUrl: string | null;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Redrive up to `maxMessages` messages from the SQS DLQ back to the main queue.
|
|
9
|
+
* Each message is received from the DLQ, re-sent to the main queue, then deleted
|
|
10
|
+
* from the DLQ. Returns the number of messages successfully redriven.
|
|
11
|
+
*/
|
|
12
|
+
export declare function redriveFromDlq(maxMessages?: number): Promise<number>;
|
|
13
|
+
export declare function startSqsWorker(): Promise<void>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SQSClient,
|
|
3
|
+
SendMessageCommand,
|
|
4
|
+
ReceiveMessageCommand,
|
|
5
|
+
DeleteMessageCommand,
|
|
6
|
+
GetQueueAttributesCommand
|
|
7
|
+
} from "@aws-sdk/client-sqs";
|
|
8
|
+
const sqs = new SQSClient({ region: process.env.AWS_REGION ?? "us-east-1" });
|
|
9
|
+
function getQueueUrl() {
|
|
10
|
+
const url = process.env.SQS_QUEUE_URL ?? process.env.SQS_CRAWL_QUEUE_URL;
|
|
11
|
+
if (!url) throw new Error("SQS_QUEUE_URL not configured");
|
|
12
|
+
return url;
|
|
13
|
+
}
|
|
14
|
+
function getDlqUrl() {
|
|
15
|
+
return process.env.SQS_DLQ_URL ?? null;
|
|
16
|
+
}
|
|
17
|
+
async function sendJobToSqs(jobId) {
|
|
18
|
+
await sqs.send(new SendMessageCommand({
|
|
19
|
+
QueueUrl: getQueueUrl(),
|
|
20
|
+
MessageBody: JSON.stringify({ jobId })
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
async function getDlqStats() {
|
|
24
|
+
const dlqUrl = getDlqUrl();
|
|
25
|
+
if (!dlqUrl) return { approximateMessageCount: 0, dlqUrl: null };
|
|
26
|
+
try {
|
|
27
|
+
const { Attributes = {} } = await sqs.send(new GetQueueAttributesCommand({
|
|
28
|
+
QueueUrl: dlqUrl,
|
|
29
|
+
AttributeNames: ["ApproximateNumberOfMessages"]
|
|
30
|
+
}));
|
|
31
|
+
return {
|
|
32
|
+
approximateMessageCount: parseInt(Attributes.ApproximateNumberOfMessages ?? "0", 10),
|
|
33
|
+
dlqUrl
|
|
34
|
+
};
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error("[sqs-dlq] getDlqStats error:", err.message);
|
|
37
|
+
return { approximateMessageCount: 0, dlqUrl };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function redriveFromDlq(maxMessages = 10) {
|
|
41
|
+
const dlqUrl = getDlqUrl();
|
|
42
|
+
if (!dlqUrl) {
|
|
43
|
+
console.warn("[sqs-dlq] SQS_DLQ_URL not configured \u2014 cannot redrive");
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
const mainUrl = getQueueUrl();
|
|
47
|
+
let totalRedriven = 0;
|
|
48
|
+
while (totalRedriven < maxMessages) {
|
|
49
|
+
const batch = Math.min(10, maxMessages - totalRedriven);
|
|
50
|
+
const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({
|
|
51
|
+
QueueUrl: dlqUrl,
|
|
52
|
+
MaxNumberOfMessages: batch,
|
|
53
|
+
WaitTimeSeconds: 1
|
|
54
|
+
}));
|
|
55
|
+
if (Messages.length === 0) break;
|
|
56
|
+
for (const msg of Messages) {
|
|
57
|
+
try {
|
|
58
|
+
await sqs.send(new SendMessageCommand({
|
|
59
|
+
QueueUrl: mainUrl,
|
|
60
|
+
MessageBody: msg.Body
|
|
61
|
+
}));
|
|
62
|
+
await sqs.send(new DeleteMessageCommand({
|
|
63
|
+
QueueUrl: dlqUrl,
|
|
64
|
+
ReceiptHandle: msg.ReceiptHandle
|
|
65
|
+
}));
|
|
66
|
+
totalRedriven++;
|
|
67
|
+
console.log("[sqs-dlq] redriven message:", msg.MessageId);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error("[sqs-dlq] redrive error for message", msg.MessageId, ":", err.message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return totalRedriven;
|
|
74
|
+
}
|
|
75
|
+
async function startSqsWorker() {
|
|
76
|
+
const queueUrl = getQueueUrl();
|
|
77
|
+
const { claimSpecificJob, dispatchClaimedJob } = await import("./job-queue.js");
|
|
78
|
+
console.log("[sqs-worker] starting, queue:", queueUrl);
|
|
79
|
+
while (true) {
|
|
80
|
+
try {
|
|
81
|
+
const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({
|
|
82
|
+
QueueUrl: queueUrl,
|
|
83
|
+
WaitTimeSeconds: 20,
|
|
84
|
+
MaxNumberOfMessages: 2
|
|
85
|
+
}));
|
|
86
|
+
await Promise.all(Messages.map(async (msg) => {
|
|
87
|
+
let jobId = "?";
|
|
88
|
+
try {
|
|
89
|
+
const data = JSON.parse(msg.Body);
|
|
90
|
+
jobId = data.jobId;
|
|
91
|
+
const claimed = await claimSpecificJob(jobId);
|
|
92
|
+
if (claimed) {
|
|
93
|
+
await dispatchClaimedJob(claimed);
|
|
94
|
+
}
|
|
95
|
+
await sqs.send(new DeleteMessageCommand({
|
|
96
|
+
QueueUrl: queueUrl,
|
|
97
|
+
ReceiptHandle: msg.ReceiptHandle
|
|
98
|
+
}));
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error(`[sqs-worker] job ${jobId} failed \u2014 leaving for SQS redrive:`, err.message);
|
|
101
|
+
}
|
|
102
|
+
}));
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error("[sqs-worker] receive error:", err.message);
|
|
105
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
getDlqStats,
|
|
111
|
+
redriveFromDlq,
|
|
112
|
+
sendJobToSqs,
|
|
113
|
+
startSqsWorker
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=sqs-queue.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/sqs-queue.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n SQSClient,\n SendMessageCommand,\n ReceiveMessageCommand,\n DeleteMessageCommand,\n GetQueueAttributesCommand,\n} from '@aws-sdk/client-sqs';\n\nconst sqs = new SQSClient({ region: process.env.AWS_REGION ?? 'us-east-1' });\n\nfunction getQueueUrl(): string {\n const url = process.env.SQS_QUEUE_URL ?? process.env.SQS_CRAWL_QUEUE_URL;\n if (!url) throw new Error('SQS_QUEUE_URL not configured');\n return url;\n}\n\nfunction getDlqUrl(): string | null {\n return process.env.SQS_DLQ_URL ?? null;\n}\n\nexport async function sendJobToSqs(jobId: string): Promise<void> {\n await sqs.send(new SendMessageCommand({\n QueueUrl: getQueueUrl(),\n MessageBody: JSON.stringify({ jobId }),\n }));\n}\n\n/** Returns the approximate number of messages in the SQS DLQ. */\nexport async function getDlqStats(): Promise<{ approximateMessageCount: number; dlqUrl: string | null }> {\n const dlqUrl = getDlqUrl();\n if (!dlqUrl) return { approximateMessageCount: 0, dlqUrl: null };\n try {\n const { Attributes = {} } = await sqs.send(new GetQueueAttributesCommand({\n QueueUrl: dlqUrl,\n AttributeNames: ['ApproximateNumberOfMessages'],\n }));\n return {\n approximateMessageCount: parseInt(Attributes.ApproximateNumberOfMessages ?? '0', 10),\n dlqUrl,\n };\n } catch (err: any) {\n console.error('[sqs-dlq] getDlqStats error:', err.message);\n return { approximateMessageCount: 0, dlqUrl };\n }\n}\n\n/**\n * Redrive up to `maxMessages` messages from the SQS DLQ back to the main queue.\n * Each message is received from the DLQ, re-sent to the main queue, then deleted\n * from the DLQ. Returns the number of messages successfully redriven.\n */\nexport async function redriveFromDlq(maxMessages = 10): Promise<number> {\n const dlqUrl = getDlqUrl();\n if (!dlqUrl) {\n console.warn('[sqs-dlq] SQS_DLQ_URL not configured \u2014 cannot redrive');\n return 0;\n }\n const mainUrl = getQueueUrl();\n let totalRedriven = 0;\n\n while (totalRedriven < maxMessages) {\n const batch = Math.min(10, maxMessages - totalRedriven);\n const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({\n QueueUrl: dlqUrl,\n MaxNumberOfMessages: batch,\n WaitTimeSeconds: 1,\n }));\n if (Messages.length === 0) break;\n\n for (const msg of Messages) {\n try {\n await sqs.send(new SendMessageCommand({\n QueueUrl: mainUrl,\n MessageBody: msg.Body!,\n }));\n await sqs.send(new DeleteMessageCommand({\n QueueUrl: dlqUrl,\n ReceiptHandle: msg.ReceiptHandle!,\n }));\n totalRedriven++;\n console.log('[sqs-dlq] redriven message:', msg.MessageId);\n } catch (err: any) {\n console.error('[sqs-dlq] redrive error for message', msg.MessageId, ':', err.message);\n }\n }\n }\n\n return totalRedriven;\n}\n\nexport async function startSqsWorker(): Promise<void> {\n const queueUrl = getQueueUrl();\n const { claimSpecificJob, dispatchClaimedJob } = await import('./job-queue.js');\n console.log('[sqs-worker] starting, queue:', queueUrl);\n\n while (true) {\n try {\n const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({\n QueueUrl: queueUrl,\n WaitTimeSeconds: 20,\n MaxNumberOfMessages: 2,\n }));\n\n await Promise.all(Messages.map(async (msg) => {\n let jobId = '?';\n try {\n const data = JSON.parse(msg.Body!);\n jobId = data.jobId;\n // Atomically move QUEUED \u2192 RUNNING; returns null if another worker beat us.\n const claimed = await claimSpecificJob(jobId);\n if (claimed) {\n await dispatchClaimedJob(claimed);\n }\n // Delete whether we ran it or another worker did \u2014 message is handled either way.\n await sqs.send(new DeleteMessageCommand({\n QueueUrl: queueUrl,\n ReceiptHandle: msg.ReceiptHandle!,\n }));\n } catch (err: any) {\n console.error(`[sqs-worker] job ${jobId} failed \u2014 leaving for SQS redrive:`, err.message);\n // Do NOT delete \u2014 SQS visibility timeout expires and DLQ handles retry.\n }\n }));\n } catch (err: any) {\n console.error('[sqs-worker] receive error:', err.message);\n await new Promise((r) => setTimeout(r, 2000));\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSO token retrieval for Okta and Azure AD.
|
|
3
|
+
* Uses OAuth2 ROPC (Resource Owner Password Credentials) flow to get access tokens
|
|
4
|
+
* without browser-based login.
|
|
5
|
+
*
|
|
6
|
+
* Note: ROPC is deprecated in OAuth 2.1 but still supported by Okta and Azure AD
|
|
7
|
+
* for service accounts and automated testing scenarios.
|
|
8
|
+
*/
|
|
9
|
+
export type SsoProvider = 'okta' | 'azure_ad' | 'generic_oidc';
|
|
10
|
+
export interface SsoConfig {
|
|
11
|
+
provider: SsoProvider;
|
|
12
|
+
/** Okta domain (e.g. 'mycompany.okta.com') or Azure tenant ID */
|
|
13
|
+
domain: string;
|
|
14
|
+
/** OAuth2 client ID */
|
|
15
|
+
clientId: string;
|
|
16
|
+
/** OAuth2 client secret (for client_credentials or confidential ROPC) */
|
|
17
|
+
clientSecret?: string;
|
|
18
|
+
/** Scopes to request (space-separated). Default: 'openid profile' */
|
|
19
|
+
scope?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get an access token using ROPC flow (username + password → token).
|
|
23
|
+
* Returns the access token string.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getSsoToken(config: SsoConfig, username: string, password: string): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Get a client credentials token (no user — for service accounts).
|
|
28
|
+
*/
|
|
29
|
+
export declare function getSsoClientCredentialsToken(config: SsoConfig): Promise<string>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
async function getSsoToken(config, username, password) {
|
|
2
|
+
let tokenUrl;
|
|
3
|
+
const params = {
|
|
4
|
+
grant_type: "password",
|
|
5
|
+
username,
|
|
6
|
+
password,
|
|
7
|
+
scope: config.scope ?? "openid profile",
|
|
8
|
+
client_id: config.clientId
|
|
9
|
+
};
|
|
10
|
+
if (config.clientSecret) {
|
|
11
|
+
params.client_secret = config.clientSecret;
|
|
12
|
+
}
|
|
13
|
+
switch (config.provider) {
|
|
14
|
+
case "okta":
|
|
15
|
+
tokenUrl = `https://${config.domain}/oauth2/v1/token`;
|
|
16
|
+
break;
|
|
17
|
+
case "azure_ad":
|
|
18
|
+
tokenUrl = `https://login.microsoftonline.com/${config.domain}/oauth2/v2.0/token`;
|
|
19
|
+
break;
|
|
20
|
+
case "generic_oidc":
|
|
21
|
+
tokenUrl = config.domain.startsWith("http") ? config.domain : `https://${config.domain}/oauth2/token`;
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
throw new Error(`Unknown SSO provider: ${config.provider}`);
|
|
25
|
+
}
|
|
26
|
+
const res = await fetch(tokenUrl, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
29
|
+
body: new URLSearchParams(params).toString()
|
|
30
|
+
});
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const body = await res.text().catch(() => res.statusText);
|
|
33
|
+
throw new Error(`SSO token request failed (${res.status}): ${body}`);
|
|
34
|
+
}
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
if (data.error) throw new Error(`SSO error: ${data.error} \u2014 ${data.error_description}`);
|
|
37
|
+
if (!data.access_token) throw new Error("SSO response missing access_token");
|
|
38
|
+
return data.access_token;
|
|
39
|
+
}
|
|
40
|
+
async function getSsoClientCredentialsToken(config) {
|
|
41
|
+
if (!config.clientSecret) throw new Error("clientSecret required for client credentials flow");
|
|
42
|
+
let tokenUrl;
|
|
43
|
+
switch (config.provider) {
|
|
44
|
+
case "okta":
|
|
45
|
+
tokenUrl = `https://${config.domain}/oauth2/v1/token`;
|
|
46
|
+
break;
|
|
47
|
+
case "azure_ad":
|
|
48
|
+
tokenUrl = `https://login.microsoftonline.com/${config.domain}/oauth2/v2.0/token`;
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
tokenUrl = `https://${config.domain}/oauth2/token`;
|
|
52
|
+
}
|
|
53
|
+
const res = await fetch(tokenUrl, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
56
|
+
body: new URLSearchParams({
|
|
57
|
+
grant_type: "client_credentials",
|
|
58
|
+
client_id: config.clientId,
|
|
59
|
+
client_secret: config.clientSecret,
|
|
60
|
+
scope: config.scope ?? "openid profile"
|
|
61
|
+
}).toString()
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
const body = await res.text().catch(() => res.statusText);
|
|
65
|
+
throw new Error(`SSO client credentials failed (${res.status}): ${body}`);
|
|
66
|
+
}
|
|
67
|
+
const data = await res.json();
|
|
68
|
+
if (data.error) throw new Error(`SSO error: ${data.error} \u2014 ${data.error_description}`);
|
|
69
|
+
if (!data.access_token) throw new Error("SSO response missing access_token");
|
|
70
|
+
return data.access_token;
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
getSsoClientCredentialsToken,
|
|
74
|
+
getSsoToken
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=sso-token.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/sso-token.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * SSO token retrieval for Okta and Azure AD.\n * Uses OAuth2 ROPC (Resource Owner Password Credentials) flow to get access tokens\n * without browser-based login.\n *\n * Note: ROPC is deprecated in OAuth 2.1 but still supported by Okta and Azure AD\n * for service accounts and automated testing scenarios.\n */\n\nexport type SsoProvider = 'okta' | 'azure_ad' | 'generic_oidc';\n\nexport interface SsoConfig {\n provider: SsoProvider;\n /** Okta domain (e.g. 'mycompany.okta.com') or Azure tenant ID */\n domain: string;\n /** OAuth2 client ID */\n clientId: string;\n /** OAuth2 client secret (for client_credentials or confidential ROPC) */\n clientSecret?: string;\n /** Scopes to request (space-separated). Default: 'openid profile' */\n scope?: string;\n}\n\n/**\n * Get an access token using ROPC flow (username + password \u2192 token).\n * Returns the access token string.\n */\nexport async function getSsoToken(\n config: SsoConfig,\n username: string,\n password: string\n): Promise<string> {\n let tokenUrl: string;\n const params: Record<string, string> = {\n grant_type: 'password',\n username,\n password,\n scope: config.scope ?? 'openid profile',\n client_id: config.clientId,\n };\n\n if (config.clientSecret) {\n params.client_secret = config.clientSecret;\n }\n\n switch (config.provider) {\n case 'okta':\n tokenUrl = `https://${config.domain}/oauth2/v1/token`;\n break;\n case 'azure_ad':\n // domain here is the tenant ID (e.g. XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX or 'mycompany.onmicrosoft.com')\n tokenUrl = `https://login.microsoftonline.com/${config.domain}/oauth2/v2.0/token`;\n break;\n case 'generic_oidc':\n tokenUrl = config.domain.startsWith('http') ? config.domain : `https://${config.domain}/oauth2/token`;\n break;\n default:\n throw new Error(`Unknown SSO provider: ${config.provider}`);\n }\n\n const res = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams(params).toString(),\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => res.statusText);\n throw new Error(`SSO token request failed (${res.status}): ${body}`);\n }\n\n const data = await res.json() as { access_token?: string; error?: string; error_description?: string };\n if (data.error) throw new Error(`SSO error: ${data.error} \u2014 ${data.error_description}`);\n if (!data.access_token) throw new Error('SSO response missing access_token');\n\n return data.access_token;\n}\n\n/**\n * Get a client credentials token (no user \u2014 for service accounts).\n */\nexport async function getSsoClientCredentialsToken(\n config: SsoConfig\n): Promise<string> {\n if (!config.clientSecret) throw new Error('clientSecret required for client credentials flow');\n\n let tokenUrl: string;\n switch (config.provider) {\n case 'okta':\n tokenUrl = `https://${config.domain}/oauth2/v1/token`;\n break;\n case 'azure_ad':\n tokenUrl = `https://login.microsoftonline.com/${config.domain}/oauth2/v2.0/token`;\n break;\n default:\n tokenUrl = `https://${config.domain}/oauth2/token`;\n }\n\n const res = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: config.clientId,\n client_secret: config.clientSecret,\n scope: config.scope ?? 'openid profile',\n }).toString(),\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => res.statusText);\n throw new Error(`SSO client credentials failed (${res.status}): ${body}`);\n }\n\n const data = await res.json() as { access_token?: string; error?: string; error_description?: string };\n if (data.error) throw new Error(`SSO error: ${data.error} \u2014 ${data.error_description}`);\n if (!data.access_token) throw new Error('SSO response missing access_token');\n\n return data.access_token;\n}\n"],
|
|
5
|
+
"mappings": "AA2BA,eAAsB,YACpB,QACA,UACA,UACiB;AACjB,MAAI;AACJ,QAAM,SAAiC;AAAA,IACrC,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,OAAO,OAAO,SAAS;AAAA,IACvB,WAAW,OAAO;AAAA,EACpB;AAEA,MAAI,OAAO,cAAc;AACvB,WAAO,gBAAgB,OAAO;AAAA,EAChC;AAEA,UAAQ,OAAO,UAAU;AAAA,IACvB,KAAK;AACH,iBAAW,WAAW,OAAO,MAAM;AACnC;AAAA,IACF,KAAK;AAEH,iBAAW,qCAAqC,OAAO,MAAM;AAC7D;AAAA,IACF,KAAK;AACH,iBAAW,OAAO,OAAO,WAAW,MAAM,IAAI,OAAO,SAAS,WAAW,OAAO,MAAM;AACtF;AAAA,IACF;AACE,YAAM,IAAI,MAAM,yBAAyB,OAAO,QAAQ,EAAE;AAAA,EAC9D;AAEA,QAAM,MAAM,MAAM,MAAM,UAAU;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,IAAI,gBAAgB,MAAM,EAAE,SAAS;AAAA,EAC7C,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,UAAU;AACxD,UAAM,IAAI,MAAM,6BAA6B,IAAI,MAAM,MAAM,IAAI,EAAE;AAAA,EACrE;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,KAAK,MAAO,OAAM,IAAI,MAAM,cAAc,KAAK,KAAK,WAAM,KAAK,iBAAiB,EAAE;AACtF,MAAI,CAAC,KAAK,aAAc,OAAM,IAAI,MAAM,mCAAmC;AAE3E,SAAO,KAAK;AACd;AAKA,eAAsB,6BACpB,QACiB;AACjB,MAAI,CAAC,OAAO,aAAc,OAAM,IAAI,MAAM,mDAAmD;AAE7F,MAAI;AACJ,UAAQ,OAAO,UAAU;AAAA,IACvB,KAAK;AACH,iBAAW,WAAW,OAAO,MAAM;AACnC;AAAA,IACF,KAAK;AACH,iBAAW,qCAAqC,OAAO,MAAM;AAC7D;AAAA,IACF;AACE,iBAAW,WAAW,OAAO,MAAM;AAAA,EACvC;AAEA,QAAM,MAAM,MAAM,MAAM,UAAU;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,IAAI,gBAAgB;AAAA,MACxB,YAAY;AAAA,MACZ,WAAW,OAAO;AAAA,MAClB,eAAe,OAAO;AAAA,MACtB,OAAO,OAAO,SAAS;AAAA,IACzB,CAAC,EAAE,SAAS;AAAA,EACd,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,UAAU;AACxD,UAAM,IAAI,MAAM,kCAAkC,IAAI,MAAM,MAAM,IAAI,EAAE;AAAA,EAC1E;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,KAAK,MAAO,OAAM,IAAI,MAAM,cAAc,KAAK,KAAK,WAAM,KAAK,iBAAiB,EAAE;AACtF,MAAI,CAAC,KAAK,aAAc,OAAM,IAAI,MAAM,mCAAmC;AAE3E,SAAO,KAAK;AACd;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
export declare function simHash32(text: string): number;
|
|
3
|
+
export declare function hammingDistance32(a: number, b: number): number;
|
|
4
|
+
export declare function normalizeUrl(raw: string): string;
|
|
5
|
+
export declare function computeStateId(normalizedUrl: string, hash: number): string;
|
|
6
|
+
export interface UIStateNode {
|
|
7
|
+
id: string;
|
|
8
|
+
url: string;
|
|
9
|
+
normalizedUrl: string;
|
|
10
|
+
title: string;
|
|
11
|
+
interactiveElementCount: number;
|
|
12
|
+
domFingerprint: string;
|
|
13
|
+
simHashValue: number;
|
|
14
|
+
visitedAt: string;
|
|
15
|
+
}
|
|
16
|
+
export interface StateTransition {
|
|
17
|
+
fromId: string;
|
|
18
|
+
toId: string;
|
|
19
|
+
action: string;
|
|
20
|
+
label: string;
|
|
21
|
+
triggeredAt: string;
|
|
22
|
+
}
|
|
23
|
+
export interface CoverageReport {
|
|
24
|
+
totalStates: number;
|
|
25
|
+
totalTransitions: number;
|
|
26
|
+
uniqueUrls: number;
|
|
27
|
+
interactiveElementsTotal: number;
|
|
28
|
+
estimatedCoveragePercent: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function fingerprintPage(page: Page, visitedAt: string): Promise<{
|
|
31
|
+
fingerprint: string;
|
|
32
|
+
visibleText: string;
|
|
33
|
+
interactiveCount: number;
|
|
34
|
+
simHashValue: number;
|
|
35
|
+
}>;
|
|
36
|
+
export declare class ScreenTransitionGraph {
|
|
37
|
+
private states;
|
|
38
|
+
private transitions;
|
|
39
|
+
/** Maps simHash value → array of state ids with that hash (for near-dup lookup) */
|
|
40
|
+
private hashIndex;
|
|
41
|
+
addState(node: UIStateNode): {
|
|
42
|
+
added: boolean;
|
|
43
|
+
duplicateOf?: string;
|
|
44
|
+
};
|
|
45
|
+
addTransition(fromId: string, toId: string, action: string, label: string, triggeredAt: string): void;
|
|
46
|
+
isDuplicate(url: string, visibleText: string, threshold?: number): boolean;
|
|
47
|
+
getCoverageReport(): CoverageReport;
|
|
48
|
+
toJSON(): {
|
|
49
|
+
states: UIStateNode[];
|
|
50
|
+
transitions: StateTransition[];
|
|
51
|
+
};
|
|
52
|
+
static fromJSON(data: {
|
|
53
|
+
states: UIStateNode[];
|
|
54
|
+
transitions: StateTransition[];
|
|
55
|
+
}): ScreenTransitionGraph;
|
|
56
|
+
getState(id: string): UIStateNode | undefined;
|
|
57
|
+
getAllStates(): UIStateNode[];
|
|
58
|
+
getTransitionsFrom(id: string): StateTransition[];
|
|
59
|
+
}
|