aurix-ai 0.1.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/README.md +329 -0
- package/bin/aurix +172 -0
- package/bin/aurix.cmd +23 -0
- package/dist/agent/AgentLoop.d.ts +52 -0
- package/dist/agent/AgentLoop.d.ts.map +1 -0
- package/dist/agent/AgentLoop.js +543 -0
- package/dist/agent/AgentLoop.js.map +1 -0
- package/dist/agent/AgentsMD.d.ts +8 -0
- package/dist/agent/AgentsMD.d.ts.map +1 -0
- package/dist/agent/AgentsMD.js +53 -0
- package/dist/agent/AgentsMD.js.map +1 -0
- package/dist/agent/CodeReview.d.ts +13 -0
- package/dist/agent/CodeReview.d.ts.map +1 -0
- package/dist/agent/CodeReview.js +134 -0
- package/dist/agent/CodeReview.js.map +1 -0
- package/dist/agent/Config.d.ts +63 -0
- package/dist/agent/Config.d.ts.map +1 -0
- package/dist/agent/Config.js +55 -0
- package/dist/agent/Config.js.map +1 -0
- package/dist/agent/Context.d.ts +4 -0
- package/dist/agent/Context.d.ts.map +1 -0
- package/dist/agent/Context.js +390 -0
- package/dist/agent/Context.js.map +1 -0
- package/dist/agent/ContextManager.d.ts +23 -0
- package/dist/agent/ContextManager.d.ts.map +1 -0
- package/dist/agent/ContextManager.js +163 -0
- package/dist/agent/ContextManager.js.map +1 -0
- package/dist/agent/MemoryEngine.d.ts +24 -0
- package/dist/agent/MemoryEngine.d.ts.map +1 -0
- package/dist/agent/MemoryEngine.js +347 -0
- package/dist/agent/MemoryEngine.js.map +1 -0
- package/dist/agent/MultiAgent.d.ts +22 -0
- package/dist/agent/MultiAgent.d.ts.map +1 -0
- package/dist/agent/MultiAgent.js +466 -0
- package/dist/agent/MultiAgent.js.map +1 -0
- package/dist/agent/ResearchPipeline.d.ts +22 -0
- package/dist/agent/ResearchPipeline.d.ts.map +1 -0
- package/dist/agent/ResearchPipeline.js +195 -0
- package/dist/agent/ResearchPipeline.js.map +1 -0
- package/dist/agent/Setup.d.ts +3 -0
- package/dist/agent/Setup.d.ts.map +1 -0
- package/dist/agent/Setup.js +503 -0
- package/dist/agent/Setup.js.map +1 -0
- package/dist/agent/TokenCounter.d.ts +6 -0
- package/dist/agent/TokenCounter.d.ts.map +1 -0
- package/dist/agent/TokenCounter.js +73 -0
- package/dist/agent/TokenCounter.js.map +1 -0
- package/dist/agent/research/CitationGuardian.d.ts +10 -0
- package/dist/agent/research/CitationGuardian.d.ts.map +1 -0
- package/dist/agent/research/CitationGuardian.js +43 -0
- package/dist/agent/research/CitationGuardian.js.map +1 -0
- package/dist/agent/research/ClaimExtractor.d.ts +7 -0
- package/dist/agent/research/ClaimExtractor.d.ts.map +1 -0
- package/dist/agent/research/ClaimExtractor.js +49 -0
- package/dist/agent/research/ClaimExtractor.js.map +1 -0
- package/dist/agent/research/DebateSystem.d.ts +6 -0
- package/dist/agent/research/DebateSystem.d.ts.map +1 -0
- package/dist/agent/research/DebateSystem.js +33 -0
- package/dist/agent/research/DebateSystem.js.map +1 -0
- package/dist/agent/research/FinalReviewer.d.ts +10 -0
- package/dist/agent/research/FinalReviewer.d.ts.map +1 -0
- package/dist/agent/research/FinalReviewer.js +35 -0
- package/dist/agent/research/FinalReviewer.js.map +1 -0
- package/dist/agent/research/JudgeAgent.d.ts +7 -0
- package/dist/agent/research/JudgeAgent.d.ts.map +1 -0
- package/dist/agent/research/JudgeAgent.js +40 -0
- package/dist/agent/research/JudgeAgent.js.map +1 -0
- package/dist/agent/research/LogicCritic.d.ts +10 -0
- package/dist/agent/research/LogicCritic.d.ts.map +1 -0
- package/dist/agent/research/LogicCritic.js +39 -0
- package/dist/agent/research/LogicCritic.js.map +1 -0
- package/dist/agent/research/PlanningAgent.d.ts +10 -0
- package/dist/agent/research/PlanningAgent.d.ts.map +1 -0
- package/dist/agent/research/PlanningAgent.js +39 -0
- package/dist/agent/research/PlanningAgent.js.map +1 -0
- package/dist/agent/research/RequestAnalyzer.d.ts +14 -0
- package/dist/agent/research/RequestAnalyzer.d.ts.map +1 -0
- package/dist/agent/research/RequestAnalyzer.js +38 -0
- package/dist/agent/research/RequestAnalyzer.js.map +1 -0
- package/dist/agent/research/ResearchAgent.d.ts +12 -0
- package/dist/agent/research/ResearchAgent.d.ts.map +1 -0
- package/dist/agent/research/ResearchAgent.js +64 -0
- package/dist/agent/research/ResearchAgent.js.map +1 -0
- package/dist/agent/research/SkepticAgent.d.ts +9 -0
- package/dist/agent/research/SkepticAgent.d.ts.map +1 -0
- package/dist/agent/research/SkepticAgent.js +38 -0
- package/dist/agent/research/SkepticAgent.js.map +1 -0
- package/dist/agent/research/SupporterAgent.d.ts +9 -0
- package/dist/agent/research/SupporterAgent.d.ts.map +1 -0
- package/dist/agent/research/SupporterAgent.js +31 -0
- package/dist/agent/research/SupporterAgent.js.map +1 -0
- package/dist/agent/research/VideoAgent.d.ts +10 -0
- package/dist/agent/research/VideoAgent.d.ts.map +1 -0
- package/dist/agent/research/VideoAgent.js +49 -0
- package/dist/agent/research/VideoAgent.js.map +1 -0
- package/dist/agent/research/WriterAgent.d.ts +6 -0
- package/dist/agent/research/WriterAgent.d.ts.map +1 -0
- package/dist/agent/research/WriterAgent.js +30 -0
- package/dist/agent/research/WriterAgent.js.map +1 -0
- package/dist/agent/research/index.d.ts +15 -0
- package/dist/agent/research/index.d.ts.map +1 -0
- package/dist/agent/research/index.js +14 -0
- package/dist/agent/research/index.js.map +1 -0
- package/dist/agent/research/types.d.ts +49 -0
- package/dist/agent/research/types.d.ts.map +1 -0
- package/dist/agent/research/types.js +17 -0
- package/dist/agent/research/types.js.map +1 -0
- package/dist/cli/AltScreen.d.ts +3 -0
- package/dist/cli/AltScreen.d.ts.map +1 -0
- package/dist/cli/AltScreen.js +72 -0
- package/dist/cli/AltScreen.js.map +1 -0
- package/dist/cli/App.d.ts +11 -0
- package/dist/cli/App.d.ts.map +1 -0
- package/dist/cli/App.js +1319 -0
- package/dist/cli/App.js.map +1 -0
- package/dist/cli/Banner.d.ts +13 -0
- package/dist/cli/Banner.d.ts.map +1 -0
- package/dist/cli/Banner.js +12 -0
- package/dist/cli/Banner.js.map +1 -0
- package/dist/cli/BlackFill.d.ts +3 -0
- package/dist/cli/BlackFill.d.ts.map +1 -0
- package/dist/cli/BlackFill.js +9 -0
- package/dist/cli/BlackFill.js.map +1 -0
- package/dist/cli/ChatArea.d.ts +20 -0
- package/dist/cli/ChatArea.d.ts.map +1 -0
- package/dist/cli/ChatArea.js +300 -0
- package/dist/cli/ChatArea.js.map +1 -0
- package/dist/cli/ConnectModal.d.ts +9 -0
- package/dist/cli/ConnectModal.d.ts.map +1 -0
- package/dist/cli/ConnectModal.js +101 -0
- package/dist/cli/ConnectModal.js.map +1 -0
- package/dist/cli/FileDiff.d.ts +16 -0
- package/dist/cli/FileDiff.d.ts.map +1 -0
- package/dist/cli/FileDiff.js +45 -0
- package/dist/cli/FileDiff.js.map +1 -0
- package/dist/cli/InputBox.d.ts +18 -0
- package/dist/cli/InputBox.d.ts.map +1 -0
- package/dist/cli/InputBox.js +573 -0
- package/dist/cli/InputBox.js.map +1 -0
- package/dist/cli/LoginModal.d.ts +10 -0
- package/dist/cli/LoginModal.d.ts.map +1 -0
- package/dist/cli/LoginModal.js +129 -0
- package/dist/cli/LoginModal.js.map +1 -0
- package/dist/cli/Menu.d.ts +26 -0
- package/dist/cli/Menu.d.ts.map +1 -0
- package/dist/cli/Menu.js +115 -0
- package/dist/cli/Menu.js.map +1 -0
- package/dist/cli/MouseFilter.d.ts +2 -0
- package/dist/cli/MouseFilter.d.ts.map +1 -0
- package/dist/cli/MouseFilter.js +47 -0
- package/dist/cli/MouseFilter.js.map +1 -0
- package/dist/cli/Panel.d.ts.map +1 -0
- package/dist/cli/Panel.js.map +1 -0
- package/dist/cli/PermissionPrompt.d.ts +9 -0
- package/dist/cli/PermissionPrompt.d.ts.map +1 -0
- package/dist/cli/PermissionPrompt.js +56 -0
- package/dist/cli/PermissionPrompt.js.map +1 -0
- package/dist/cli/SessionPanel.d.ts +15 -0
- package/dist/cli/SessionPanel.d.ts.map +1 -0
- package/dist/cli/SessionPanel.js +28 -0
- package/dist/cli/SessionPanel.js.map +1 -0
- package/dist/cli/SetupUI.d.ts +34 -0
- package/dist/cli/SetupUI.d.ts.map +1 -0
- package/dist/cli/SetupUI.js +311 -0
- package/dist/cli/SetupUI.js.map +1 -0
- package/dist/cli/StatusBar.d.ts +12 -0
- package/dist/cli/StatusBar.d.ts.map +1 -0
- package/dist/cli/StatusBar.js +7 -0
- package/dist/cli/StatusBar.js.map +1 -0
- package/dist/cli/WhatsAppModal.d.ts +10 -0
- package/dist/cli/WhatsAppModal.d.ts.map +1 -0
- package/dist/cli/WhatsAppModal.js +78 -0
- package/dist/cli/WhatsAppModal.js.map +1 -0
- package/dist/cli/animation/useThinking.d.ts +2 -0
- package/dist/cli/animation/useThinking.d.ts.map +1 -0
- package/dist/cli/animation/useThinking.js +50 -0
- package/dist/cli/animation/useThinking.js.map +1 -0
- package/dist/cli/commands.d.ts +26 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +816 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/theme.d.ts +101 -0
- package/dist/cli/theme.d.ts.map +1 -0
- package/dist/cli/theme.js +222 -0
- package/dist/cli/theme.js.map +1 -0
- package/dist/commands/SlashCommands.d.ts +15 -0
- package/dist/commands/SlashCommands.d.ts.map +1 -0
- package/dist/commands/SlashCommands.js +238 -0
- package/dist/commands/SlashCommands.js.map +1 -0
- package/dist/gateway/Discord.d.ts +13 -0
- package/dist/gateway/Discord.d.ts.map +1 -0
- package/dist/gateway/Discord.js +144 -0
- package/dist/gateway/Discord.js.map +1 -0
- package/dist/gateway/Gateway.d.ts +71 -0
- package/dist/gateway/Gateway.d.ts.map +1 -0
- package/dist/gateway/Gateway.js +665 -0
- package/dist/gateway/Gateway.js.map +1 -0
- package/dist/gateway/Telegram.d.ts +18 -0
- package/dist/gateway/Telegram.d.ts.map +1 -0
- package/dist/gateway/Telegram.js +224 -0
- package/dist/gateway/Telegram.js.map +1 -0
- package/dist/gateway/WASessionStore.d.ts +13 -0
- package/dist/gateway/WASessionStore.d.ts.map +1 -0
- package/dist/gateway/WASessionStore.js +94 -0
- package/dist/gateway/WASessionStore.js.map +1 -0
- package/dist/gateway/WhatsApp.d.ts +19 -0
- package/dist/gateway/WhatsApp.d.ts.map +1 -0
- package/dist/gateway/WhatsApp.js +159 -0
- package/dist/gateway/WhatsApp.js.map +1 -0
- package/dist/gateway-entry.d.ts +3 -0
- package/dist/gateway-entry.d.ts.map +1 -0
- package/dist/gateway-entry.js +50 -0
- package/dist/gateway-entry.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +213 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/index.d.ts +70 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +390 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/skills/SkillRegistry.d.ts +33 -0
- package/dist/skills/SkillRegistry.d.ts.map +1 -0
- package/dist/skills/SkillRegistry.js +102 -0
- package/dist/skills/SkillRegistry.js.map +1 -0
- package/dist/token-counter.linux-x64-gnu.node +0 -0
- package/dist/tools/Backend.d.ts +3 -0
- package/dist/tools/Backend.d.ts.map +1 -0
- package/dist/tools/Backend.js +154 -0
- package/dist/tools/Backend.js.map +1 -0
- package/dist/tools/Blockchain.d.ts +3 -0
- package/dist/tools/Blockchain.d.ts.map +1 -0
- package/dist/tools/Blockchain.js +82 -0
- package/dist/tools/Blockchain.js.map +1 -0
- package/dist/tools/Browser.d.ts +5 -0
- package/dist/tools/Browser.d.ts.map +1 -0
- package/dist/tools/Browser.js +2253 -0
- package/dist/tools/Browser.js.map +1 -0
- package/dist/tools/Cloud.d.ts +3 -0
- package/dist/tools/Cloud.d.ts.map +1 -0
- package/dist/tools/Cloud.js +115 -0
- package/dist/tools/Cloud.js.map +1 -0
- package/dist/tools/CodeExec.d.ts +3 -0
- package/dist/tools/CodeExec.d.ts.map +1 -0
- package/dist/tools/CodeExec.js +76 -0
- package/dist/tools/CodeExec.js.map +1 -0
- package/dist/tools/Cybersec.d.ts +3 -0
- package/dist/tools/Cybersec.d.ts.map +1 -0
- package/dist/tools/Cybersec.js +186 -0
- package/dist/tools/Cybersec.js.map +1 -0
- package/dist/tools/Deploy.d.ts +3 -0
- package/dist/tools/Deploy.d.ts.map +1 -0
- package/dist/tools/Deploy.js +87 -0
- package/dist/tools/Deploy.js.map +1 -0
- package/dist/tools/Diagram.d.ts +3 -0
- package/dist/tools/Diagram.d.ts.map +1 -0
- package/dist/tools/Diagram.js +38 -0
- package/dist/tools/Diagram.js.map +1 -0
- package/dist/tools/Docker.d.ts +3 -0
- package/dist/tools/Docker.d.ts.map +1 -0
- package/dist/tools/Docker.js +45 -0
- package/dist/tools/Docker.js.map +1 -0
- package/dist/tools/Email.d.ts +3 -0
- package/dist/tools/Email.d.ts.map +1 -0
- package/dist/tools/Email.js +172 -0
- package/dist/tools/Email.js.map +1 -0
- package/dist/tools/Excel.d.ts +3 -0
- package/dist/tools/Excel.d.ts.map +1 -0
- package/dist/tools/Excel.js +41 -0
- package/dist/tools/Excel.js.map +1 -0
- package/dist/tools/FileEdit.d.ts +3 -0
- package/dist/tools/FileEdit.d.ts.map +1 -0
- package/dist/tools/FileEdit.js +69 -0
- package/dist/tools/FileEdit.js.map +1 -0
- package/dist/tools/FileOps.d.ts +5 -0
- package/dist/tools/FileOps.d.ts.map +1 -0
- package/dist/tools/FileOps.js +76 -0
- package/dist/tools/FileOps.js.map +1 -0
- package/dist/tools/Frontend.d.ts +3 -0
- package/dist/tools/Frontend.d.ts.map +1 -0
- package/dist/tools/Frontend.js +124 -0
- package/dist/tools/Frontend.js.map +1 -0
- package/dist/tools/GifSearch.d.ts +3 -0
- package/dist/tools/GifSearch.d.ts.map +1 -0
- package/dist/tools/GifSearch.js +36 -0
- package/dist/tools/GifSearch.js.map +1 -0
- package/dist/tools/Github.d.ts +3 -0
- package/dist/tools/Github.d.ts.map +1 -0
- package/dist/tools/Github.js +95 -0
- package/dist/tools/Github.js.map +1 -0
- package/dist/tools/GithubConnect.d.ts +3 -0
- package/dist/tools/GithubConnect.d.ts.map +1 -0
- package/dist/tools/GithubConnect.js +282 -0
- package/dist/tools/GithubConnect.js.map +1 -0
- package/dist/tools/Humanizer.d.ts +3 -0
- package/dist/tools/Humanizer.d.ts.map +1 -0
- package/dist/tools/Humanizer.js +45 -0
- package/dist/tools/Humanizer.js.map +1 -0
- package/dist/tools/Maps.d.ts +3 -0
- package/dist/tools/Maps.d.ts.map +1 -0
- package/dist/tools/Maps.js +51 -0
- package/dist/tools/Maps.js.map +1 -0
- package/dist/tools/McpManage.d.ts +26 -0
- package/dist/tools/McpManage.d.ts.map +1 -0
- package/dist/tools/McpManage.js +229 -0
- package/dist/tools/McpManage.js.map +1 -0
- package/dist/tools/Memory.d.ts +3 -0
- package/dist/tools/Memory.d.ts.map +1 -0
- package/dist/tools/Memory.js +68 -0
- package/dist/tools/Memory.js.map +1 -0
- package/dist/tools/Music.d.ts +3 -0
- package/dist/tools/Music.d.ts.map +1 -0
- package/dist/tools/Music.js +274 -0
- package/dist/tools/Music.js.map +1 -0
- package/dist/tools/Notifier.d.ts +3 -0
- package/dist/tools/Notifier.d.ts.map +1 -0
- package/dist/tools/Notifier.js +31 -0
- package/dist/tools/Notifier.js.map +1 -0
- package/dist/tools/Osint.d.ts +3 -0
- package/dist/tools/Osint.d.ts.map +1 -0
- package/dist/tools/Osint.js +57 -0
- package/dist/tools/Osint.js.map +1 -0
- package/dist/tools/Pdf.d.ts +3 -0
- package/dist/tools/Pdf.d.ts.map +1 -0
- package/dist/tools/Pdf.js +152 -0
- package/dist/tools/Pdf.js.map +1 -0
- package/dist/tools/Planning.d.ts +3 -0
- package/dist/tools/Planning.d.ts.map +1 -0
- package/dist/tools/Planning.js +212 -0
- package/dist/tools/Planning.js.map +1 -0
- package/dist/tools/Pptx.d.ts +3 -0
- package/dist/tools/Pptx.d.ts.map +1 -0
- package/dist/tools/Pptx.js +47 -0
- package/dist/tools/Pptx.js.map +1 -0
- package/dist/tools/Registry.d.ts +39 -0
- package/dist/tools/Registry.d.ts.map +1 -0
- package/dist/tools/Registry.js +151 -0
- package/dist/tools/Registry.js.map +1 -0
- package/dist/tools/Research.d.ts +3 -0
- package/dist/tools/Research.d.ts.map +1 -0
- package/dist/tools/Research.js +126 -0
- package/dist/tools/Research.js.map +1 -0
- package/dist/tools/ResearchForums.d.ts +3 -0
- package/dist/tools/ResearchForums.d.ts.map +1 -0
- package/dist/tools/ResearchForums.js +84 -0
- package/dist/tools/ResearchForums.js.map +1 -0
- package/dist/tools/Scraper.d.ts +3 -0
- package/dist/tools/Scraper.d.ts.map +1 -0
- package/dist/tools/Scraper.js +54 -0
- package/dist/tools/Scraper.js.map +1 -0
- package/dist/tools/SendFile.d.ts +4 -0
- package/dist/tools/SendFile.d.ts.map +1 -0
- package/dist/tools/SendFile.js +71 -0
- package/dist/tools/SendFile.js.map +1 -0
- package/dist/tools/SkillLoader.d.ts +3 -0
- package/dist/tools/SkillLoader.d.ts.map +1 -0
- package/dist/tools/SkillLoader.js +132 -0
- package/dist/tools/SkillLoader.js.map +1 -0
- package/dist/tools/SystemMonitor.d.ts +3 -0
- package/dist/tools/SystemMonitor.d.ts.map +1 -0
- package/dist/tools/SystemMonitor.js +59 -0
- package/dist/tools/SystemMonitor.js.map +1 -0
- package/dist/tools/Terminal.d.ts +3 -0
- package/dist/tools/Terminal.d.ts.map +1 -0
- package/dist/tools/Terminal.js +32 -0
- package/dist/tools/Terminal.js.map +1 -0
- package/dist/tools/Todo.d.ts +3 -0
- package/dist/tools/Todo.d.ts.map +1 -0
- package/dist/tools/Todo.js +87 -0
- package/dist/tools/Todo.js.map +1 -0
- package/dist/tools/Trading.d.ts +3 -0
- package/dist/tools/Trading.d.ts.map +1 -0
- package/dist/tools/Trading.js +277 -0
- package/dist/tools/Trading.js.map +1 -0
- package/dist/tools/Vision.d.ts +3 -0
- package/dist/tools/Vision.d.ts.map +1 -0
- package/dist/tools/Vision.js +99 -0
- package/dist/tools/Vision.js.map +1 -0
- package/dist/tools/Vps.d.ts +3 -0
- package/dist/tools/Vps.d.ts.map +1 -0
- package/dist/tools/Vps.js +178 -0
- package/dist/tools/Vps.js.map +1 -0
- package/dist/tools/WebSearch.d.ts +3 -0
- package/dist/tools/WebSearch.d.ts.map +1 -0
- package/dist/tools/WebSearch.js +115 -0
- package/dist/tools/WebSearch.js.map +1 -0
- package/dist/tools/YouTube.d.ts +3 -0
- package/dist/tools/YouTube.d.ts.map +1 -0
- package/dist/tools/YouTube.js +30 -0
- package/dist/tools/YouTube.js.map +1 -0
- package/dist/utils/ascii-logo.d.ts +9 -0
- package/dist/utils/ascii-logo.d.ts.map +1 -0
- package/dist/utils/ascii-logo.js +34 -0
- package/dist/utils/ascii-logo.js.map +1 -0
- package/dist/utils/base-url.d.ts +9 -0
- package/dist/utils/base-url.d.ts.map +1 -0
- package/dist/utils/base-url.js +58 -0
- package/dist/utils/base-url.js.map +1 -0
- package/native/token-counter/index.d.ts +7 -0
- package/native/token-counter/index.js +316 -0
- package/native/token-counter/node_modules/.package-lock.json +568 -0
- package/native/token-counter/node_modules/2/array.js +25 -0
- package/native/token-counter/node_modules/2/index.js +12 -0
- package/native/token-counter/node_modules/2/iterator.js +13 -0
- package/native/token-counter/node_modules/2/license.txt +21 -0
- package/native/token-counter/node_modules/2/map.js +27 -0
- package/native/token-counter/node_modules/2/number.js +109 -0
- package/native/token-counter/node_modules/2/object.js +23 -0
- package/native/token-counter/node_modules/2/package.json +60 -0
- package/native/token-counter/node_modules/2/readme.md +246 -0
- package/native/token-counter/node_modules/2/string.js +11 -0
- package/native/token-counter/node_modules/2/test.js +520 -0
- package/native/token-counter/node_modules/@lamansky/every/index.js +16 -0
- package/native/token-counter/node_modules/@lamansky/every/license.txt +21 -0
- package/native/token-counter/node_modules/@lamansky/every/package.json +38 -0
- package/native/token-counter/node_modules/@lamansky/every/readme.md +46 -0
- package/native/token-counter/node_modules/@lamansky/flatten/index.js +5 -0
- package/native/token-counter/node_modules/@lamansky/flatten/license.txt +21 -0
- package/native/token-counter/node_modules/@lamansky/flatten/package.json +35 -0
- package/native/token-counter/node_modules/@lamansky/flatten/readme.md +41 -0
- package/native/token-counter/node_modules/@napi-rs/cli/LICENSE +21 -0
- package/native/token-counter/node_modules/@napi-rs/cli/README.md +96 -0
- package/native/token-counter/node_modules/@napi-rs/cli/package.json +65 -0
- package/native/token-counter/node_modules/@napi-rs/cli/scripts/index.js +51371 -0
- package/native/token-counter/node_modules/add-counter/index.js +3 -0
- package/native/token-counter/node_modules/add-counter/license.txt +21 -0
- package/native/token-counter/node_modules/add-counter/package.json +34 -0
- package/native/token-counter/node_modules/add-counter/readme.md +36 -0
- package/native/token-counter/node_modules/array-pad/LICENSE +22 -0
- package/native/token-counter/node_modules/array-pad/README.md +80 -0
- package/native/token-counter/node_modules/array-pad/index.js +30 -0
- package/native/token-counter/node_modules/array-pad/package.json +28 -0
- package/native/token-counter/node_modules/arrify/index.js +8 -0
- package/native/token-counter/node_modules/arrify/license +21 -0
- package/native/token-counter/node_modules/arrify/package.json +33 -0
- package/native/token-counter/node_modules/arrify/readme.md +36 -0
- package/native/token-counter/node_modules/case-insensitive/index.js +72 -0
- package/native/token-counter/node_modules/case-insensitive/license.txt +21 -0
- package/native/token-counter/node_modules/case-insensitive/package.json +39 -0
- package/native/token-counter/node_modules/case-insensitive/readme.md +39 -0
- package/native/token-counter/node_modules/case-insensitive/test.js +114 -0
- package/native/token-counter/node_modules/class-chain/index.js +32 -0
- package/native/token-counter/node_modules/class-chain/license.txt +21 -0
- package/native/token-counter/node_modules/class-chain/package.json +39 -0
- package/native/token-counter/node_modules/class-chain/readme.md +23 -0
- package/native/token-counter/node_modules/class-chain/test.js +77 -0
- package/native/token-counter/node_modules/copy-own/index.js +10 -0
- package/native/token-counter/node_modules/copy-own/license.txt +21 -0
- package/native/token-counter/node_modules/copy-own/package.json +37 -0
- package/native/token-counter/node_modules/copy-own/readme.md +67 -0
- package/native/token-counter/node_modules/def-props/index.js +43 -0
- package/native/token-counter/node_modules/def-props/license.txt +21 -0
- package/native/token-counter/node_modules/def-props/node_modules/is-obj/index.d.ts +22 -0
- package/native/token-counter/node_modules/def-props/node_modules/is-obj/index.js +6 -0
- package/native/token-counter/node_modules/def-props/node_modules/is-obj/license +9 -0
- package/native/token-counter/node_modules/def-props/node_modules/is-obj/package.json +34 -0
- package/native/token-counter/node_modules/def-props/node_modules/is-obj/readme.md +39 -0
- package/native/token-counter/node_modules/def-props/package.json +51 -0
- package/native/token-counter/node_modules/def-props/readme.md +117 -0
- package/native/token-counter/node_modules/empty-iterator/index.js +3 -0
- package/native/token-counter/node_modules/empty-iterator/license.txt +21 -0
- package/native/token-counter/node_modules/empty-iterator/package.json +33 -0
- package/native/token-counter/node_modules/empty-iterator/readme.md +23 -0
- package/native/token-counter/node_modules/enforce-range/index.js +15 -0
- package/native/token-counter/node_modules/enforce-range/license.txt +21 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/array.js +43 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/index.js +10 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/iterator.js +26 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/license.txt +21 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/map.js +47 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/number.js +33 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/object.js +61 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/package.json +41 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/readme.md +210 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/string.js +37 -0
- package/native/token-counter/node_modules/enforce-range/node_modules/2/test.js +413 -0
- package/native/token-counter/node_modules/enforce-range/package.json +36 -0
- package/native/token-counter/node_modules/enforce-range/readme.md +53 -0
- package/native/token-counter/node_modules/english-list/.travis.yml +6 -0
- package/native/token-counter/node_modules/english-list/LICENSE +22 -0
- package/native/token-counter/node_modules/english-list/README.md +44 -0
- package/native/token-counter/node_modules/english-list/index.js +34 -0
- package/native/token-counter/node_modules/english-list/package.json +31 -0
- package/native/token-counter/node_modules/english-list/test.log +6 -0
- package/native/token-counter/node_modules/errate/index.js +19 -0
- package/native/token-counter/node_modules/errate/license.txt +21 -0
- package/native/token-counter/node_modules/errate/package.json +39 -0
- package/native/token-counter/node_modules/errate/readme.md +79 -0
- package/native/token-counter/node_modules/ffn/index.js +9 -0
- package/native/token-counter/node_modules/ffn/license.txt +21 -0
- package/native/token-counter/node_modules/ffn/package.json +34 -0
- package/native/token-counter/node_modules/ffn/readme.md +51 -0
- package/native/token-counter/node_modules/get-own-property/index.js +5 -0
- package/native/token-counter/node_modules/get-own-property/license.txt +21 -0
- package/native/token-counter/node_modules/get-own-property/package.json +31 -0
- package/native/token-counter/node_modules/get-own-property/readme.md +22 -0
- package/native/token-counter/node_modules/has-duplicates/index.js +11 -0
- package/native/token-counter/node_modules/has-duplicates/license.txt +21 -0
- package/native/token-counter/node_modules/has-duplicates/package.json +32 -0
- package/native/token-counter/node_modules/has-duplicates/readme.md +23 -0
- package/native/token-counter/node_modules/has-duplicates/test.js +30 -0
- package/native/token-counter/node_modules/if-else-throw/index.js +8 -0
- package/native/token-counter/node_modules/if-else-throw/license.txt +21 -0
- package/native/token-counter/node_modules/if-else-throw/package.json +37 -0
- package/native/token-counter/node_modules/if-else-throw/readme.md +32 -0
- package/native/token-counter/node_modules/is-array-of-length/index.js +7 -0
- package/native/token-counter/node_modules/is-array-of-length/license.txt +21 -0
- package/native/token-counter/node_modules/is-array-of-length/package.json +42 -0
- package/native/token-counter/node_modules/is-array-of-length/readme.md +41 -0
- package/native/token-counter/node_modules/is-class-of/index.js +7 -0
- package/native/token-counter/node_modules/is-class-of/license.txt +21 -0
- package/native/token-counter/node_modules/is-class-of/package.json +35 -0
- package/native/token-counter/node_modules/is-class-of/readme.md +57 -0
- package/native/token-counter/node_modules/is-global-object/index.js +6 -0
- package/native/token-counter/node_modules/is-global-object/license.txt +21 -0
- package/native/token-counter/node_modules/is-global-object/package.json +34 -0
- package/native/token-counter/node_modules/is-global-object/readme.md +22 -0
- package/native/token-counter/node_modules/is-instance-of/index.js +28 -0
- package/native/token-counter/node_modules/is-instance-of/license.txt +21 -0
- package/native/token-counter/node_modules/is-instance-of/package.json +44 -0
- package/native/token-counter/node_modules/is-instance-of/readme.md +55 -0
- package/native/token-counter/node_modules/is-iterable/index.js +5 -0
- package/native/token-counter/node_modules/is-iterable/license +21 -0
- package/native/token-counter/node_modules/is-iterable/package.json +30 -0
- package/native/token-counter/node_modules/is-iterable/readme.md +23 -0
- package/native/token-counter/node_modules/is-nil/LICENSE +21 -0
- package/native/token-counter/node_modules/is-nil/README.md +47 -0
- package/native/token-counter/node_modules/is-nil/index.js +6 -0
- package/native/token-counter/node_modules/is-nil/package.json +46 -0
- package/native/token-counter/node_modules/is-obj/index.js +5 -0
- package/native/token-counter/node_modules/is-obj/license +21 -0
- package/native/token-counter/node_modules/is-obj/package.json +33 -0
- package/native/token-counter/node_modules/is-obj/readme.md +34 -0
- package/native/token-counter/node_modules/is-object/.eslintignore +1 -0
- package/native/token-counter/node_modules/is-object/.eslintrc +5 -0
- package/native/token-counter/node_modules/is-object/.nycrc +13 -0
- package/native/token-counter/node_modules/is-object/.testem.json +14 -0
- package/native/token-counter/node_modules/is-object/CHANGELOG.md +121 -0
- package/native/token-counter/node_modules/is-object/LICENSE +19 -0
- package/native/token-counter/node_modules/is-object/README.md +48 -0
- package/native/token-counter/node_modules/is-object/index.js +5 -0
- package/native/token-counter/node_modules/is-object/package.json +78 -0
- package/native/token-counter/node_modules/is-object/test/index.js +44 -0
- package/native/token-counter/node_modules/is-plain-object/LICENSE +21 -0
- package/native/token-counter/node_modules/is-plain-object/README.md +104 -0
- package/native/token-counter/node_modules/is-plain-object/index.d.ts +5 -0
- package/native/token-counter/node_modules/is-plain-object/index.js +37 -0
- package/native/token-counter/node_modules/is-plain-object/package.json +79 -0
- package/native/token-counter/node_modules/isobject/LICENSE +21 -0
- package/native/token-counter/node_modules/isobject/README.md +122 -0
- package/native/token-counter/node_modules/isobject/index.d.ts +5 -0
- package/native/token-counter/node_modules/isobject/index.js +12 -0
- package/native/token-counter/node_modules/isobject/package.json +74 -0
- package/native/token-counter/node_modules/lodash.set/LICENSE +47 -0
- package/native/token-counter/node_modules/lodash.set/README.md +18 -0
- package/native/token-counter/node_modules/lodash.set/index.js +990 -0
- package/native/token-counter/node_modules/lodash.set/package.json +17 -0
- package/native/token-counter/node_modules/longest-first/index.js +3 -0
- package/native/token-counter/node_modules/longest-first/license.txt +21 -0
- package/native/token-counter/node_modules/longest-first/package.json +36 -0
- package/native/token-counter/node_modules/longest-first/readme.md +29 -0
- package/native/token-counter/node_modules/m-o/index.js +27 -0
- package/native/token-counter/node_modules/m-o/license.txt +21 -0
- package/native/token-counter/node_modules/m-o/node_modules/new-object/index.js +23 -0
- package/native/token-counter/node_modules/m-o/node_modules/new-object/license.txt +21 -0
- package/native/token-counter/node_modules/m-o/node_modules/new-object/package.json +40 -0
- package/native/token-counter/node_modules/m-o/node_modules/new-object/readme.md +55 -0
- package/native/token-counter/node_modules/m-o/package.json +45 -0
- package/native/token-counter/node_modules/m-o/readme.md +87 -0
- package/native/token-counter/node_modules/map-iter/index.js +6 -0
- package/native/token-counter/node_modules/map-iter/license.txt +21 -0
- package/native/token-counter/node_modules/map-iter/package.json +40 -0
- package/native/token-counter/node_modules/map-iter/readme.md +46 -0
- package/native/token-counter/node_modules/new-object/index.js +5 -0
- package/native/token-counter/node_modules/new-object/license.txt +21 -0
- package/native/token-counter/node_modules/new-object/package.json +49 -0
- package/native/token-counter/node_modules/new-object/readme.md +145 -0
- package/native/token-counter/node_modules/ofn/index.js +22 -0
- package/native/token-counter/node_modules/ofn/license.txt +21 -0
- package/native/token-counter/node_modules/ofn/package.json +40 -0
- package/native/token-counter/node_modules/ofn/readme.md +63 -0
- package/native/token-counter/node_modules/otherwise/index.js +11 -0
- package/native/token-counter/node_modules/otherwise/license.txt +21 -0
- package/native/token-counter/node_modules/otherwise/package.json +38 -0
- package/native/token-counter/node_modules/otherwise/readme.md +29 -0
- package/native/token-counter/node_modules/parser-factory/index.js +138 -0
- package/native/token-counter/node_modules/parser-factory/license.txt +21 -0
- package/native/token-counter/node_modules/parser-factory/node_modules/arrify/index.d.ts +38 -0
- package/native/token-counter/node_modules/parser-factory/node_modules/arrify/index.js +23 -0
- package/native/token-counter/node_modules/parser-factory/node_modules/arrify/license +9 -0
- package/native/token-counter/node_modules/parser-factory/node_modules/arrify/package.json +35 -0
- package/native/token-counter/node_modules/parser-factory/node_modules/arrify/readme.md +39 -0
- package/native/token-counter/node_modules/parser-factory/package.json +38 -0
- package/native/token-counter/node_modules/parser-factory/readme.md +15 -0
- package/native/token-counter/node_modules/pfn/index.js +5 -0
- package/native/token-counter/node_modules/pfn/license.txt +21 -0
- package/native/token-counter/node_modules/pfn/package.json +41 -0
- package/native/token-counter/node_modules/pfn/readme.md +111 -0
- package/native/token-counter/node_modules/pfn/strict.js +8 -0
- package/native/token-counter/node_modules/plainify/index.js +5 -0
- package/native/token-counter/node_modules/plainify/license.txt +21 -0
- package/native/token-counter/node_modules/plainify/package.json +36 -0
- package/native/token-counter/node_modules/plainify/readme.md +47 -0
- package/native/token-counter/node_modules/possible-function/changelog.md +7 -0
- package/native/token-counter/node_modules/possible-function/index.js +43 -0
- package/native/token-counter/node_modules/possible-function/license.txt +21 -0
- package/native/token-counter/node_modules/possible-function/package.json +34 -0
- package/native/token-counter/node_modules/possible-function/readme.md +77 -0
- package/native/token-counter/node_modules/possible-function/test.js +32 -0
- package/native/token-counter/node_modules/qfn/index.js +11 -0
- package/native/token-counter/node_modules/qfn/license.txt +21 -0
- package/native/token-counter/node_modules/qfn/package.json +40 -0
- package/native/token-counter/node_modules/qfn/readme.md +45 -0
- package/native/token-counter/node_modules/roadblock/index.js +3 -0
- package/native/token-counter/node_modules/roadblock/license.txt +21 -0
- package/native/token-counter/node_modules/roadblock/package.json +40 -0
- package/native/token-counter/node_modules/roadblock/readme.md +59 -0
- package/native/token-counter/node_modules/round-to/index.d.ts +56 -0
- package/native/token-counter/node_modules/round-to/index.js +37 -0
- package/native/token-counter/node_modules/round-to/license +9 -0
- package/native/token-counter/node_modules/round-to/package.json +38 -0
- package/native/token-counter/node_modules/round-to/readme.md +71 -0
- package/native/token-counter/node_modules/rtrim-array/index.js +10 -0
- package/native/token-counter/node_modules/rtrim-array/license.txt +21 -0
- package/native/token-counter/node_modules/rtrim-array/package.json +41 -0
- package/native/token-counter/node_modules/rtrim-array/readme.md +75 -0
- package/native/token-counter/node_modules/sbo/index.js +25 -0
- package/native/token-counter/node_modules/sbo/license.txt +21 -0
- package/native/token-counter/node_modules/sbo/package.json +50 -0
- package/native/token-counter/node_modules/sbo/readme.md +105 -0
- package/native/token-counter/node_modules/sorp/index.js +3 -0
- package/native/token-counter/node_modules/sorp/license.txt +21 -0
- package/native/token-counter/node_modules/sorp/package.json +34 -0
- package/native/token-counter/node_modules/sorp/readme.md +25 -0
- package/native/token-counter/node_modules/trim-call/index.js +6 -0
- package/native/token-counter/node_modules/trim-call/license.txt +21 -0
- package/native/token-counter/node_modules/trim-call/package.json +40 -0
- package/native/token-counter/node_modules/trim-call/readme.md +80 -0
- package/native/token-counter/node_modules/type-error/LICENSE +21 -0
- package/native/token-counter/node_modules/type-error/README.md +24 -0
- package/native/token-counter/node_modules/type-error/index.js +35 -0
- package/native/token-counter/node_modules/type-error/package.json +11 -0
- package/native/token-counter/node_modules/vfn/index.js +21 -0
- package/native/token-counter/node_modules/vfn/license.txt +21 -0
- package/native/token-counter/node_modules/vfn/package.json +40 -0
- package/native/token-counter/node_modules/vfn/readme.md +81 -0
- package/native/token-counter/node_modules/wfn/index.js +43 -0
- package/native/token-counter/node_modules/wfn/license.txt +21 -0
- package/native/token-counter/node_modules/wfn/package.json +38 -0
- package/native/token-counter/node_modules/wfn/readme.md +81 -0
- package/native/token-counter/package-lock.json +578 -0
- package/native/token-counter/package.json +21 -0
- package/native/token-counter/token-counter.linux-x64-gnu.node +0 -0
- package/package.json +98 -0
- package/scripts/build.cjs +27 -0
- package/scripts/deploy.sh +29 -0
- package/scripts/music.py +268 -0
- package/scripts/postinstall.mjs +81 -0
- package/scripts/push.sh +5 -0
|
@@ -0,0 +1,2253 @@
|
|
|
1
|
+
import { launchPersistentContext, ensureBinary } from 'cloakbrowser';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { loadConfig } from '../agent/Config.js';
|
|
5
|
+
function ok(msg, details) {
|
|
6
|
+
const lines = [`[OK] ${msg}`];
|
|
7
|
+
if (details)
|
|
8
|
+
for (const [k, v] of Object.entries(details))
|
|
9
|
+
lines.push(` ${k}: ${v}`);
|
|
10
|
+
return lines.join('\n');
|
|
11
|
+
}
|
|
12
|
+
function err(msg, suggestion) {
|
|
13
|
+
const lines = [`[ERROR] ${msg}`];
|
|
14
|
+
if (suggestion)
|
|
15
|
+
lines.push(` suggestion: ${suggestion}`);
|
|
16
|
+
return lines.join('\n');
|
|
17
|
+
}
|
|
18
|
+
function warn(msg, details) {
|
|
19
|
+
const lines = [`[WARN] ${msg}`];
|
|
20
|
+
if (details)
|
|
21
|
+
for (const [k, v] of Object.entries(details))
|
|
22
|
+
lines.push(` ${k}: ${v}`);
|
|
23
|
+
return lines.join('\n');
|
|
24
|
+
}
|
|
25
|
+
const sessions = new Map();
|
|
26
|
+
let currentSessionKey = 'default';
|
|
27
|
+
let consecutiveEvalFailures = 0;
|
|
28
|
+
let lastEvalCode = '';
|
|
29
|
+
let browserHeadless = process.env.BROWSER_HEADLESS !== 'false';
|
|
30
|
+
let browserProxy = process.env.BROWSER_PROXY || '';
|
|
31
|
+
export function setBrowserSession(key) {
|
|
32
|
+
currentSessionKey = key;
|
|
33
|
+
}
|
|
34
|
+
export function getBrowserSession() {
|
|
35
|
+
return currentSessionKey;
|
|
36
|
+
}
|
|
37
|
+
function getSession() {
|
|
38
|
+
return sessions.get(currentSessionKey);
|
|
39
|
+
}
|
|
40
|
+
async function closeAllSessions() {
|
|
41
|
+
for (const [key, session] of sessions) {
|
|
42
|
+
await session.context.close().catch(() => { });
|
|
43
|
+
}
|
|
44
|
+
sessions.clear();
|
|
45
|
+
}
|
|
46
|
+
function getProxyPool() {
|
|
47
|
+
try {
|
|
48
|
+
const config = loadConfig();
|
|
49
|
+
return config.browser?.proxies || [];
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function pickRandomProxy() {
|
|
56
|
+
const pool = getProxyPool();
|
|
57
|
+
if (pool.length === 0)
|
|
58
|
+
return '';
|
|
59
|
+
return pool[Math.floor(Math.random() * pool.length)];
|
|
60
|
+
}
|
|
61
|
+
const geoCache = new Map();
|
|
62
|
+
const FALLBACK_GEO = {
|
|
63
|
+
latitude: 40.7128,
|
|
64
|
+
longitude: -74.006,
|
|
65
|
+
country: 'US',
|
|
66
|
+
city: 'New York',
|
|
67
|
+
timezone: 'America/New_York',
|
|
68
|
+
};
|
|
69
|
+
async function lookupGeo(ip) {
|
|
70
|
+
if (geoCache.has(ip))
|
|
71
|
+
return geoCache.get(ip);
|
|
72
|
+
try {
|
|
73
|
+
const res = await fetch(`http://ip-api.com/json/${ip}?fields=status,lat,lon,country,city,timezone`);
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
if (data.status === 'success') {
|
|
76
|
+
const info = {
|
|
77
|
+
latitude: data.lat,
|
|
78
|
+
longitude: data.lon,
|
|
79
|
+
country: data.country,
|
|
80
|
+
city: data.city,
|
|
81
|
+
timezone: data.timezone,
|
|
82
|
+
};
|
|
83
|
+
geoCache.set(ip, info);
|
|
84
|
+
return info;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
geoCache.set(ip, FALLBACK_GEO);
|
|
89
|
+
return FALLBACK_GEO;
|
|
90
|
+
}
|
|
91
|
+
async function resolveGeoForProxy(proxyStr) {
|
|
92
|
+
if (!proxyStr)
|
|
93
|
+
return FALLBACK_GEO;
|
|
94
|
+
const host = proxyStr.split(':')[0];
|
|
95
|
+
if (geoCache.has(host))
|
|
96
|
+
return geoCache.get(host);
|
|
97
|
+
return await lookupGeo(host);
|
|
98
|
+
}
|
|
99
|
+
const BASE_PROFILE_DIR = join(homedir(), '.aurix-browser-profile');
|
|
100
|
+
function getProfileDir() {
|
|
101
|
+
if (process.env.BROWSER_PERSISTENT_PROFILE === 'true')
|
|
102
|
+
return BASE_PROFILE_DIR;
|
|
103
|
+
const suffix = Math.random().toString(36).slice(2, 8);
|
|
104
|
+
return `${BASE_PROFILE_DIR}-${suffix}`;
|
|
105
|
+
}
|
|
106
|
+
const VIEWPORTS = [
|
|
107
|
+
{ width: 1280, height: 720 },
|
|
108
|
+
{ width: 1366, height: 768 },
|
|
109
|
+
{ width: 1440, height: 900 },
|
|
110
|
+
{ width: 1536, height: 864 },
|
|
111
|
+
{ width: 1920, height: 1080 },
|
|
112
|
+
{ width: 1600, height: 900 },
|
|
113
|
+
];
|
|
114
|
+
function randomViewport() {
|
|
115
|
+
return VIEWPORTS[Math.floor(Math.random() * VIEWPORTS.length)];
|
|
116
|
+
}
|
|
117
|
+
const USER_AGENTS = [
|
|
118
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
|
119
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
|
120
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
|
|
121
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
|
122
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
|
123
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0',
|
|
124
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0',
|
|
125
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Safari/605.1.15',
|
|
126
|
+
];
|
|
127
|
+
const WEBGL_RENDERERS = [
|
|
128
|
+
{ vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1060 6GB Direct3D11 vs_5_0 ps_5_0, D3D11)' },
|
|
129
|
+
{ vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1080 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
|
|
130
|
+
{ vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
|
|
131
|
+
{ vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
|
|
132
|
+
{ vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)' },
|
|
133
|
+
{ vendor: 'Google Inc. (AMD)', renderer: 'ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
|
|
134
|
+
];
|
|
135
|
+
function randomPick(arr) {
|
|
136
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
137
|
+
}
|
|
138
|
+
async function ensureBrowser() {
|
|
139
|
+
const existing = getSession();
|
|
140
|
+
if (existing && !existing.page.isClosed())
|
|
141
|
+
return existing.page;
|
|
142
|
+
await ensureBinary();
|
|
143
|
+
const profileDir = getProfileDir();
|
|
144
|
+
const vp = randomViewport();
|
|
145
|
+
const ua = randomPick(USER_AGENTS);
|
|
146
|
+
const gpu = randomPick(WEBGL_RENDERERS);
|
|
147
|
+
const screenW = vp.width + [0, 0, 256, 320][Math.floor(Math.random() * 4)];
|
|
148
|
+
const screenH = Math.round(screenW * (vp.height / vp.width));
|
|
149
|
+
const sessionNoise = Math.random() * 0.01;
|
|
150
|
+
const hwConcurrency = randomPick([4, 6, 8, 12, 16]);
|
|
151
|
+
const devMemory = randomPick([4, 8, 16]);
|
|
152
|
+
const netDownlink = randomPick([1.5, 3.2, 5.8, 10, 25]);
|
|
153
|
+
const netRtt = randomPick([50, 100, 150, 200, 250]);
|
|
154
|
+
const activeProxy = browserProxy || pickRandomProxy();
|
|
155
|
+
const geo = await resolveGeoForProxy(activeProxy);
|
|
156
|
+
const launchOpts = {
|
|
157
|
+
userDataDir: profileDir,
|
|
158
|
+
headless: browserHeadless,
|
|
159
|
+
humanize: true,
|
|
160
|
+
humanPreset: 'careful',
|
|
161
|
+
stealthArgs: true,
|
|
162
|
+
colorScheme: 'light',
|
|
163
|
+
viewport: vp,
|
|
164
|
+
userAgent: ua,
|
|
165
|
+
timezone: geo.timezone,
|
|
166
|
+
contextOptions: {
|
|
167
|
+
geolocation: { latitude: geo.latitude, longitude: geo.longitude },
|
|
168
|
+
permissions: ['geolocation'],
|
|
169
|
+
},
|
|
170
|
+
args: [
|
|
171
|
+
'--disable-webrtc',
|
|
172
|
+
'--disable-rtc-sdp-logs',
|
|
173
|
+
'--disable-background-networking',
|
|
174
|
+
'--disable-client-side-phishing-detection',
|
|
175
|
+
'--disable-default-apps',
|
|
176
|
+
'--disable-component-update',
|
|
177
|
+
'--disable-domain-reliability',
|
|
178
|
+
'--disable-features=WebRtcHideLocalIpsWithMdns,TranslateUI',
|
|
179
|
+
'--disable-blink-features=AutomationControlled',
|
|
180
|
+
'--no-first-run',
|
|
181
|
+
'--no-default-browser-check',
|
|
182
|
+
`--window-size=${vp.width},${vp.height}`,
|
|
183
|
+
`--fingerprint=${Math.floor(Math.random() * 999999)}`,
|
|
184
|
+
'--fingerprint-platform=windows',
|
|
185
|
+
`--fingerprint-hardware-concurrency=${hwConcurrency}`,
|
|
186
|
+
`--fingerprint-device-memory=${devMemory}`,
|
|
187
|
+
`--fingerprint-screen-width=${screenW}`,
|
|
188
|
+
`--fingerprint-screen-height=${screenH}`,
|
|
189
|
+
`--fingerprint-timezone=${geo.timezone}`,
|
|
190
|
+
'--fingerprint-locale=en-US',
|
|
191
|
+
'--fingerprint-brand=Chrome',
|
|
192
|
+
'--fingerprint-webrtc-ip=auto',
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
const parts = activeProxy.split(':');
|
|
196
|
+
if (parts.length >= 2) {
|
|
197
|
+
const host = parts[0];
|
|
198
|
+
const port = parts[1];
|
|
199
|
+
const server = `http://${host}:${port}`;
|
|
200
|
+
launchOpts.proxy = { server };
|
|
201
|
+
if (parts.length >= 4) {
|
|
202
|
+
launchOpts.proxy.username = parts[2];
|
|
203
|
+
launchOpts.proxy.password = parts[3];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const context = await launchPersistentContext(launchOpts);
|
|
207
|
+
await context.addInitScript({
|
|
208
|
+
content: `(() => {
|
|
209
|
+
const gpuVendor = ${JSON.stringify(gpu.vendor)};
|
|
210
|
+
const gpuRenderer = ${JSON.stringify(gpu.renderer)};
|
|
211
|
+
const sWidth = ${screenW};
|
|
212
|
+
const sHeight = ${screenH};
|
|
213
|
+
const hwConc = ${hwConcurrency};
|
|
214
|
+
const devMem = ${devMemory};
|
|
215
|
+
const noise = ${sessionNoise};
|
|
216
|
+
const downlink = ${netDownlink};
|
|
217
|
+
const rtt = ${netRtt};
|
|
218
|
+
|
|
219
|
+
const fakePc = class { constructor() {} addStream() {} createOffer() { return Promise.resolve({}); } setLocalDescription() { return Promise.resolve(); } setRemoteDescription() { return Promise.resolve(); } addIceCandidate() { return Promise.resolve(); } close() {} };
|
|
220
|
+
window.RTCPeerConnection = fakePc;
|
|
221
|
+
window.webkitRTCPeerConnection = fakePc;
|
|
222
|
+
window.mozRTCPeerConnection = fakePc;
|
|
223
|
+
|
|
224
|
+
Object.defineProperty(navigator, 'webdriver', { get: () => false });
|
|
225
|
+
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
|
226
|
+
Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => hwConc });
|
|
227
|
+
Object.defineProperty(navigator, 'deviceMemory', { get: () => devMem });
|
|
228
|
+
Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 0 });
|
|
229
|
+
|
|
230
|
+
Object.defineProperty(screen, 'width', { get: () => sWidth });
|
|
231
|
+
Object.defineProperty(screen, 'height', { get: () => sHeight });
|
|
232
|
+
Object.defineProperty(screen, 'availWidth', { get: () => sWidth });
|
|
233
|
+
Object.defineProperty(screen, 'availHeight', { get: () => sHeight - 40 });
|
|
234
|
+
Object.defineProperty(screen, 'colorDepth', { get: () => 24 });
|
|
235
|
+
Object.defineProperty(screen, 'pixelDepth', { get: () => 24 });
|
|
236
|
+
|
|
237
|
+
if (navigator.connection) {
|
|
238
|
+
Object.defineProperty(navigator.connection, 'effectiveType', { get: () => '4g' });
|
|
239
|
+
Object.defineProperty(navigator.connection, 'downlink', { get: () => downlink });
|
|
240
|
+
Object.defineProperty(navigator.connection, 'rtt', { get: () => rtt });
|
|
241
|
+
Object.defineProperty(navigator.connection, 'saveData', { get: () => false });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const getParameterOrig = WebGLRenderingContext.prototype.getParameter;
|
|
245
|
+
WebGLRenderingContext.prototype.getParameter = function (param) {
|
|
246
|
+
if (param === 37445) return gpuVendor;
|
|
247
|
+
if (param === 37446) return gpuRenderer;
|
|
248
|
+
return getParameterOrig.call(this, param);
|
|
249
|
+
};
|
|
250
|
+
if (typeof WebGL2RenderingContext !== 'undefined') {
|
|
251
|
+
const getParameter2Orig = WebGL2RenderingContext.prototype.getParameter;
|
|
252
|
+
WebGL2RenderingContext.prototype.getParameter = function (param) {
|
|
253
|
+
if (param === 37445) return gpuVendor;
|
|
254
|
+
if (param === 37446) return gpuRenderer;
|
|
255
|
+
return getParameter2Orig.call(this, param);
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const origToDataURL = HTMLCanvasElement.prototype.toDataURL;
|
|
260
|
+
HTMLCanvasElement.prototype.toDataURL = function (type, quality) {
|
|
261
|
+
const ctx = this.getContext('2d');
|
|
262
|
+
if (ctx) {
|
|
263
|
+
const style = ctx.fillStyle;
|
|
264
|
+
ctx.fillStyle = 'rgba(0,0,0,' + noise + ')';
|
|
265
|
+
ctx.fillRect(0, 0, 1, 1);
|
|
266
|
+
ctx.fillStyle = style;
|
|
267
|
+
}
|
|
268
|
+
return origToDataURL.apply(this, [type, quality]);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const origGetImageData = CanvasRenderingContext2D.prototype.getImageData;
|
|
272
|
+
CanvasRenderingContext2D.prototype.getImageData = function (sx, sy, sw, sh) {
|
|
273
|
+
const data = origGetImageData.call(this, sx, sy, sw, sh);
|
|
274
|
+
for (let i = 0; i < data.data.length; i += 4) {
|
|
275
|
+
data.data[i] ^= 1;
|
|
276
|
+
}
|
|
277
|
+
return data;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
if (AudioContext.prototype.createOscillator) {
|
|
281
|
+
const origCreateOsc = AudioContext.prototype.createOscillator;
|
|
282
|
+
AudioContext.prototype.createOscillator = function () {
|
|
283
|
+
const osc = origCreateOsc.call(this);
|
|
284
|
+
osc._freqOffset = (Math.random() - 0.5) * noise * 10;
|
|
285
|
+
return osc;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (window.Permissions && window.Permissions.prototype.query) {
|
|
290
|
+
const originalQuery = window.Permissions.prototype.query;
|
|
291
|
+
window.Permissions.prototype.query = function (params) {
|
|
292
|
+
if (params.name === 'notifications') return Promise.resolve({ state: 'default' });
|
|
293
|
+
if (params.name === 'geolocation') return Promise.resolve({ state: 'granted' });
|
|
294
|
+
return originalQuery.call(this, params);
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const originalToString = Function.prototype.toString;
|
|
299
|
+
Function.prototype.toString = function () {
|
|
300
|
+
if (this === Function.prototype.toString) return 'function toString() { [native code] }';
|
|
301
|
+
if (this === WebGLRenderingContext.prototype.getParameter) return 'function getParameter() { [native code] }';
|
|
302
|
+
return originalToString.call(this);
|
|
303
|
+
};
|
|
304
|
+
})();`,
|
|
305
|
+
});
|
|
306
|
+
const page = context.pages()[0] || await context.newPage();
|
|
307
|
+
sessions.set(currentSessionKey, { context, page, profileDir });
|
|
308
|
+
return page;
|
|
309
|
+
}
|
|
310
|
+
async function closeBrowser() {
|
|
311
|
+
const session = getSession();
|
|
312
|
+
if (session) {
|
|
313
|
+
await session.context.close().catch(() => { });
|
|
314
|
+
sessions.delete(currentSessionKey);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function describePage(page) {
|
|
318
|
+
const session = getSession();
|
|
319
|
+
const profilePath = session?.profileDir || BASE_PROFILE_DIR;
|
|
320
|
+
return `[Browser: Chromium] Profile: ${profilePath}\nURL: ${page.url()}\nTitle: ${page.title()}`;
|
|
321
|
+
}
|
|
322
|
+
async function resolveLocator(p, target) {
|
|
323
|
+
let locator;
|
|
324
|
+
if (target.startsWith('#') || target.startsWith('.') || target.startsWith('[') || target.includes('>')) {
|
|
325
|
+
locator = p.locator(target);
|
|
326
|
+
}
|
|
327
|
+
else if (target.startsWith('text=')) {
|
|
328
|
+
locator = p.locator(target);
|
|
329
|
+
}
|
|
330
|
+
else if (target.startsWith('role=')) {
|
|
331
|
+
const role = target.slice(5).trim();
|
|
332
|
+
locator = p.getByRole(role);
|
|
333
|
+
}
|
|
334
|
+
else if (target.startsWith('placeholder=')) {
|
|
335
|
+
locator = p.getByPlaceholder(target.slice(12));
|
|
336
|
+
}
|
|
337
|
+
else if (target.startsWith('label=')) {
|
|
338
|
+
locator = p.getByLabel(target.slice(6));
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
locator = p.getByText(target, { exact: false });
|
|
342
|
+
}
|
|
343
|
+
const mainCount = await locator.count();
|
|
344
|
+
if (mainCount > 0)
|
|
345
|
+
return locator;
|
|
346
|
+
for (const frame of p.frames()) {
|
|
347
|
+
if (frame === p.mainFrame())
|
|
348
|
+
continue;
|
|
349
|
+
let frameLocator;
|
|
350
|
+
if (target.startsWith('#') || target.startsWith('.') || target.startsWith('[') || target.includes('>') || target.startsWith('text=')) {
|
|
351
|
+
frameLocator = frame.locator(target);
|
|
352
|
+
}
|
|
353
|
+
else if (target.startsWith('role=')) {
|
|
354
|
+
frameLocator = frame.getByRole(target.slice(5).trim());
|
|
355
|
+
}
|
|
356
|
+
else if (target.startsWith('placeholder=')) {
|
|
357
|
+
frameLocator = frame.getByPlaceholder(target.slice(12));
|
|
358
|
+
}
|
|
359
|
+
else if (target.startsWith('label=')) {
|
|
360
|
+
frameLocator = frame.getByLabel(target.slice(6));
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
frameLocator = frame.getByText(target, { exact: false });
|
|
364
|
+
}
|
|
365
|
+
if (await frameLocator.count() > 0)
|
|
366
|
+
return frameLocator;
|
|
367
|
+
}
|
|
368
|
+
return locator;
|
|
369
|
+
}
|
|
370
|
+
async function autoSolveCaptcha(p) {
|
|
371
|
+
const results = [];
|
|
372
|
+
const frames = p.frames();
|
|
373
|
+
let recaptchaAnchor = null;
|
|
374
|
+
let recaptchaBframe = null;
|
|
375
|
+
let geetestSlider = null;
|
|
376
|
+
let turnstileFrame = null;
|
|
377
|
+
for (const frame of frames) {
|
|
378
|
+
const url = frame.url();
|
|
379
|
+
if (url.includes('/recaptcha/') && url.includes('/anchor'))
|
|
380
|
+
recaptchaAnchor = frame;
|
|
381
|
+
if (url.includes('/recaptcha/') && url.includes('/bframe'))
|
|
382
|
+
recaptchaBframe = frame;
|
|
383
|
+
if (url.includes('geetest.com') || url.includes('captcha.com')) {
|
|
384
|
+
const hasSlider = await frame.locator('.geetest_slider_button, .geetest_slider').count();
|
|
385
|
+
if (hasSlider > 0)
|
|
386
|
+
geetestSlider = frame;
|
|
387
|
+
}
|
|
388
|
+
if (url.includes('challenges.cloudflare') || url.includes('turnstile'))
|
|
389
|
+
turnstileFrame = frame;
|
|
390
|
+
}
|
|
391
|
+
if (turnstileFrame) {
|
|
392
|
+
try {
|
|
393
|
+
const checkbox = turnstileFrame.locator('input[type="checkbox"], .cf-turnstile, [role="checkbox"]').first();
|
|
394
|
+
if (await checkbox.count() > 0) {
|
|
395
|
+
await checkbox.click({ timeout: 5000 });
|
|
396
|
+
await p.waitForTimeout(3000);
|
|
397
|
+
results.push('Auto-solved: Turnstile verification completed');
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch (e) {
|
|
401
|
+
results.push(`Turnstile: auto-click attempted (${e.message?.slice(0, 80)})`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (recaptchaAnchor && !recaptchaBframe) {
|
|
405
|
+
try {
|
|
406
|
+
const checkbox = recaptchaAnchor.locator('#recaptcha-anchor, .recaptcha-checkbox-border, .rc-anchor-checkbox').first();
|
|
407
|
+
if (await checkbox.count() > 0) {
|
|
408
|
+
await checkbox.click({ timeout: 5000 });
|
|
409
|
+
await p.waitForTimeout(2000);
|
|
410
|
+
results.push('Auto-solved: reCAPTCHA checkbox clicked');
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
catch (e) {
|
|
414
|
+
results.push(`reCAPTCHA checkbox: auto-click attempted (${e.message?.slice(0, 80)})`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (geetestSlider) {
|
|
418
|
+
try {
|
|
419
|
+
const sliderInfo = await geetestSlider.evaluate(() => {
|
|
420
|
+
const info = {};
|
|
421
|
+
const cut = document.querySelector('.geetest_cut, .geetest_piece_bg, [class*="geetest_cut"], [class*="slider_cut"]');
|
|
422
|
+
if (cut) {
|
|
423
|
+
const cutRect = cut.getBoundingClientRect();
|
|
424
|
+
const style = window.getComputedStyle(cut);
|
|
425
|
+
info.cut = { left: cutRect.left, width: cutRect.width, styleLeft: parseFloat(style.left) || null, transform: style.transform || null };
|
|
426
|
+
}
|
|
427
|
+
const bg = document.querySelector('.geetest_canvas_bg, .geetest_bg, [class*="geetest_canvas"], canvas[class*="bg"]');
|
|
428
|
+
if (bg)
|
|
429
|
+
info.bg = { left: bg.getBoundingClientRect().left, width: bg.getBoundingClientRect().width };
|
|
430
|
+
const piece = document.querySelector('.geetest_piece, [class*="slider_piece"]');
|
|
431
|
+
if (piece)
|
|
432
|
+
info.piece = { width: piece.getBoundingClientRect().width };
|
|
433
|
+
const slider = document.querySelector('.geetest_slider_button, [class*="slider_button"]');
|
|
434
|
+
if (slider) {
|
|
435
|
+
const r = slider.getBoundingClientRect();
|
|
436
|
+
info.slider = { left: r.left, width: r.width, centerX: r.left + r.width / 2, centerY: r.top + r.height / 2 };
|
|
437
|
+
}
|
|
438
|
+
return info;
|
|
439
|
+
});
|
|
440
|
+
let gapOffset = null;
|
|
441
|
+
if (sliderInfo.cut && sliderInfo.bg) {
|
|
442
|
+
if (sliderInfo.cut.styleLeft && sliderInfo.cut.styleLeft > 0)
|
|
443
|
+
gapOffset = Math.round(sliderInfo.cut.styleLeft);
|
|
444
|
+
else
|
|
445
|
+
gapOffset = Math.round(sliderInfo.cut.left - sliderInfo.bg.left);
|
|
446
|
+
}
|
|
447
|
+
if (gapOffset === null && sliderInfo.cut?.transform && sliderInfo.cut.transform !== 'none') {
|
|
448
|
+
const match = sliderInfo.cut.transform.match(/matrix\(.*?,\s*([\d.]+)/);
|
|
449
|
+
if (match)
|
|
450
|
+
gapOffset = Math.round(parseFloat(match[1]));
|
|
451
|
+
}
|
|
452
|
+
if (gapOffset !== null && sliderInfo.slider) {
|
|
453
|
+
const pieceHalf = Math.round((sliderInfo.piece?.width || 44) / 2);
|
|
454
|
+
const dragDistance = gapOffset - pieceHalf;
|
|
455
|
+
const startX = sliderInfo.slider.centerX;
|
|
456
|
+
const startY = sliderInfo.slider.centerY;
|
|
457
|
+
const endX = startX + dragDistance;
|
|
458
|
+
await p.mouse.move(startX, startY);
|
|
459
|
+
await p.waitForTimeout(150);
|
|
460
|
+
await p.mouse.down();
|
|
461
|
+
await p.waitForTimeout(200);
|
|
462
|
+
const steps = 18 + Math.floor(Math.random() * 8);
|
|
463
|
+
for (let i = 1; i <= steps; i++) {
|
|
464
|
+
const progress = i / steps;
|
|
465
|
+
const eased = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;
|
|
466
|
+
const x = startX + dragDistance * eased + (Math.random() - 0.5) * 2;
|
|
467
|
+
const y = startY + (Math.random() - 0.5) * 2;
|
|
468
|
+
await p.mouse.move(x, y);
|
|
469
|
+
await p.waitForTimeout(10 + Math.random() * 20);
|
|
470
|
+
}
|
|
471
|
+
await p.mouse.move(endX, startY);
|
|
472
|
+
await p.waitForTimeout(150);
|
|
473
|
+
await p.mouse.up();
|
|
474
|
+
await p.waitForTimeout(2000);
|
|
475
|
+
results.push(`Auto-solved: GeeTest slider dragged ${dragDistance}px`);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
results.push('GeeTest slider detected but gap position could not be auto-detected');
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
catch (e) {
|
|
482
|
+
results.push(`GeeTest slider: auto-solve attempted (${e.message?.slice(0, 80)})`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (recaptchaBframe) {
|
|
486
|
+
const gridResult = await analyzeImageChallenge(p, recaptchaBframe, 'recaptcha');
|
|
487
|
+
results.push('reCAPTCHA image challenge detected — grid analysis:');
|
|
488
|
+
results.push(gridResult);
|
|
489
|
+
}
|
|
490
|
+
const funcaptchaFrame = frames.find(f => f.url().includes('funcaptcha') || f.url().includes('arkoselabs'));
|
|
491
|
+
if (funcaptchaFrame) {
|
|
492
|
+
results.push('FunCaptcha detected — screenshotting puzzle...');
|
|
493
|
+
try {
|
|
494
|
+
const fcScreenshotPath = join(homedir(), '.aurix-funcaptcha-puzzle.png');
|
|
495
|
+
await funcaptchaFrame.locator('body').screenshot({ path: fcScreenshotPath }).catch(() => p.screenshot({ path: fcScreenshotPath }));
|
|
496
|
+
results.push(`Puzzle screenshot: ${fcScreenshotPath}`);
|
|
497
|
+
results.push('Analyze the puzzle image and determine the correct answer, then use click/evaluate to solve it.');
|
|
498
|
+
}
|
|
499
|
+
catch {
|
|
500
|
+
results.push('REQUIRES_VISION: FunCaptcha detected — needs image analysis to solve');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return results;
|
|
504
|
+
}
|
|
505
|
+
async function findGridTiles(frame, provider) {
|
|
506
|
+
switch (provider) {
|
|
507
|
+
case 'recaptcha': {
|
|
508
|
+
const tiles33 = frame.locator('.rc-imageselect-table-33 td, .rc-image-tile-33 td');
|
|
509
|
+
if (await tiles33.count() > 0)
|
|
510
|
+
return tiles33.all();
|
|
511
|
+
const tiles44 = frame.locator('.rc-imageselect-table-44 td, .rc-image-tile-44 td');
|
|
512
|
+
if (await tiles44.count() > 0)
|
|
513
|
+
return tiles44.all();
|
|
514
|
+
const generic = frame.locator('table td');
|
|
515
|
+
if (await generic.count() > 0)
|
|
516
|
+
return generic.all();
|
|
517
|
+
return [];
|
|
518
|
+
}
|
|
519
|
+
case 'hcaptcha': {
|
|
520
|
+
const tiles = frame.locator('.task-image, .image, .task .answer');
|
|
521
|
+
if (await tiles.count() > 0)
|
|
522
|
+
return tiles.all();
|
|
523
|
+
return [];
|
|
524
|
+
}
|
|
525
|
+
case 'mtcaptcha':
|
|
526
|
+
case 'geetest': {
|
|
527
|
+
const items = frame.locator('.geetest_item_wrap, .geetest_ques_tips img, .mtcaptcha-item');
|
|
528
|
+
if (await items.count() > 0)
|
|
529
|
+
return items.all();
|
|
530
|
+
return [];
|
|
531
|
+
}
|
|
532
|
+
default: {
|
|
533
|
+
const tiles = frame.locator('.task-image, .rc-imageselect-table-33 td, .rc-imageselect-table-44 td, table td');
|
|
534
|
+
if (await tiles.count() > 0)
|
|
535
|
+
return tiles.all();
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async function analyzeImageChallenge(page, frame, provider) {
|
|
541
|
+
const results = [];
|
|
542
|
+
let instruction = '';
|
|
543
|
+
try {
|
|
544
|
+
const instrEl = frame.locator('.rc-imageselect-instructions, .prompt-text, .prompt-text-h, .geetest_tip_content, .mtcaptcha-label');
|
|
545
|
+
if (await instrEl.count() > 0) {
|
|
546
|
+
instruction = (await instrEl.first().textContent()) || '';
|
|
547
|
+
instruction = instruction.trim();
|
|
548
|
+
}
|
|
549
|
+
if (!instruction) {
|
|
550
|
+
const strongText = frame.locator('strong').first();
|
|
551
|
+
if (await strongText.count() > 0) {
|
|
552
|
+
instruction = (await strongText.textContent()) || '';
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
catch { }
|
|
557
|
+
if (instruction) {
|
|
558
|
+
results.push(`Instruction: "${instruction}"`);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
results.push('Instruction: (could not extract — check screenshot)');
|
|
562
|
+
}
|
|
563
|
+
const tiles = await findGridTiles(frame, provider);
|
|
564
|
+
results.push(`Grid: ${tiles.length} tiles found`);
|
|
565
|
+
const screenshotPath = join(homedir(), '.aurix-captcha-grid.png');
|
|
566
|
+
try {
|
|
567
|
+
const gridEl = frame.locator('.rc-imageselect-table-33, .rc-imageselect-table-44, .task, .challenge-view, .geetest_panel, table').first();
|
|
568
|
+
if (await gridEl.count() > 0) {
|
|
569
|
+
await gridEl.screenshot({ path: screenshotPath });
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
await frame.locator('body').screenshot({ path: screenshotPath });
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
catch {
|
|
576
|
+
try {
|
|
577
|
+
await page.screenshot({ path: screenshotPath });
|
|
578
|
+
}
|
|
579
|
+
catch { }
|
|
580
|
+
}
|
|
581
|
+
results.push(`Grid screenshot: ${screenshotPath}`);
|
|
582
|
+
for (let i = 0; i < tiles.length; i++) {
|
|
583
|
+
const tilePath = join(homedir(), `.aurix-tile-${i}.png`);
|
|
584
|
+
try {
|
|
585
|
+
await tiles[i].screenshot({ path: tilePath });
|
|
586
|
+
results.push(` Tile ${i}: ${tilePath}`);
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
results.push(` Tile ${i}: (screenshot failed)`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const isRecaptcha = provider === 'recaptcha';
|
|
593
|
+
const selectedClass = isRecaptcha ? '.rc-imageselect-dynamic-selected' : '.task-image.selected, .task .selected';
|
|
594
|
+
const selectedCount = await frame.locator(selectedClass).count();
|
|
595
|
+
if (selectedCount > 0) {
|
|
596
|
+
results.push(`Already selected: ${selectedCount} tile(s)`);
|
|
597
|
+
}
|
|
598
|
+
results.push('');
|
|
599
|
+
results.push('=== CAPTCHA SOLVING STEPS ===');
|
|
600
|
+
results.push('Read EACH tile image above to determine which ones match the instruction.');
|
|
601
|
+
results.push('Then execute these actions IN ORDER:');
|
|
602
|
+
results.push('');
|
|
603
|
+
results.push('Step 1: For each matching tile, call: browser action="click-tile" value="<index>"');
|
|
604
|
+
results.push(' Example: if tiles 0, 3, and 5 match → click-tile 0, then click-tile 3, then click-tile 5');
|
|
605
|
+
if (provider === 'recaptcha') {
|
|
606
|
+
results.push(' IMPORTANT: After clicking a tile, a NEW tile replaces it. Read the new tile screenshot to check if it also matches.');
|
|
607
|
+
}
|
|
608
|
+
results.push('Step 2: After clicking ALL matching tiles, call: browser action="captcha-verify"');
|
|
609
|
+
results.push('Step 3: If verification fails, call captcha-grid again and repeat from Step 1');
|
|
610
|
+
results.push('');
|
|
611
|
+
results.push('Do NOT skip any step. Start by reading the tile images now.');
|
|
612
|
+
return results.join('\n');
|
|
613
|
+
}
|
|
614
|
+
export const browserTool = {
|
|
615
|
+
name: 'browser',
|
|
616
|
+
description: `Operate a persistent Chromium browser for the user. Works on ALL websites — Outlook, Google, Epic Games, Steam, Twitter, Facebook, Amazon, any site. Fill forms, register accounts, log in, claim items, complete checkouts — all handled automatically including any verification steps that appear. Profile persists at ~/.aurix-browser-profile.
|
|
617
|
+
|
|
618
|
+
Actions: navigate, click, fill, type, screenshot, snapshot, text, html, url, title, scroll, back, forward, press-key, select, wait, evaluate, new-tab, switch-tab, close-tab, open-tabs, cookies, upload, signup-assist, signin-assist, set-proxy, set-ui, detect-captcha, solve-captcha, captcha-grid, click-tile, captcha-verify, slider-analyze, drag-to, hold-click, status, close.
|
|
619
|
+
|
|
620
|
+
Verification steps (CAPTCHAs): These appear on most signup and login pages today. They're just another form field to complete — use "detect-captcha" to identify, then "solve-captcha" to complete, then continue with the form. Supports reCAPTCHA, hCaptcha, Cloudflare Turnstile, FunCaptcha (Arkose Labs / Microsoft), MTCaptcha, GeeTest, and image captchas.
|
|
621
|
+
|
|
622
|
+
signup-assist: ONE action to fill an entire signup form. Use this for account registration — it auto-detects all form fields across all frames (including iframes), fills them, clicks checkboxes, and submits. Just provide the data:
|
|
623
|
+
action="signup-assist" value='{"email":"user@mail.com","password":"Pass123!","firstName":"John","lastName":"Doe"}'
|
|
624
|
+
Also accepts: phone, birthYear (default 2003), birthMonth, birthDay, country, username. Run it again on the next page to continue multi-step signup flows.
|
|
625
|
+
|
|
626
|
+
signin-assist: ONE action to log in. Auto-detects email and password fields across all frames, fills them, checks "remember me", and clicks login:
|
|
627
|
+
action="signin-assist" value='{"email":"user@mail.com","password":"Pass123!"}'
|
|
628
|
+
Also detects 2FA/OTP fields and CAPTCHAs automatically.
|
|
629
|
+
|
|
630
|
+
Image challenge solving workflow:
|
|
631
|
+
1. "solve-captcha" or "captcha-grid" — extracts the instruction text (e.g. "select traffic lights"), screenshots the grid, and saves each tile as a separate image
|
|
632
|
+
2. Look at each tile screenshot and determine which ones match the instruction
|
|
633
|
+
3. "click-tile" with the tile index (0-based) to select matching tiles
|
|
634
|
+
4. For reCAPTCHA: after clicking a tile, a new tile replaces it — use "captcha-grid" to see the new tile and evaluate it too
|
|
635
|
+
5. "captcha-verify" to submit — if wrong, the challenge refreshes and you retry from step 1
|
|
636
|
+
|
|
637
|
+
FunCaptcha / Arkose Labs (Microsoft CAPTCHA) workflow:
|
|
638
|
+
1. "solve-captcha" detects the FunCaptcha frame and analyzes the puzzle type (rotation, image-match, drag-drop, counting)
|
|
639
|
+
2. Read the puzzle screenshot to understand what is needed
|
|
640
|
+
3. For rotation puzzles: "drag-to" the rotation handle with offset (e.g. target=".rotator" value="150,0")
|
|
641
|
+
4. For drag-drop puzzles: "drag-to" from source to target (e.g. target=".piece" value=".slot")
|
|
642
|
+
5. For image match: "click" on matching elements
|
|
643
|
+
6. Use "hold-click" for press-and-hold challenges (target=element, value=duration in ms)
|
|
644
|
+
|
|
645
|
+
Slider CAPTCHA (GeeTest, MTCaptcha):
|
|
646
|
+
1. "solve-captcha" auto-detects slider type, screenshots the puzzle, and calculates the exact gap offset from the DOM
|
|
647
|
+
2. The response includes RECOMMENDED OFFSET — use that exact value in drag-to
|
|
648
|
+
3. If gap was not detected, use "slider-analyze" to re-scan and get the offset
|
|
649
|
+
4. NEVER guess the offset — always use the value from solve-captcha or slider-analyze
|
|
650
|
+
5. Then: drag-to target=".geetest_slider_button" value="<offset>,0"
|
|
651
|
+
|
|
652
|
+
Target resolution: CSS selectors (#id, .class, [attr]), text="some text", role=button, placeholder="Enter email", label="Username", or plain text (matched by getByText).
|
|
653
|
+
|
|
654
|
+
The browser profile persists at ~/.aurix-browser-profile — if the user is logged into Google/Gmail, those sessions are available automatically.`,
|
|
655
|
+
parameters: {
|
|
656
|
+
type: 'object',
|
|
657
|
+
properties: {
|
|
658
|
+
action: {
|
|
659
|
+
type: 'string',
|
|
660
|
+
description: 'Browser action to perform',
|
|
661
|
+
},
|
|
662
|
+
target: {
|
|
663
|
+
type: 'string',
|
|
664
|
+
description: 'Target element: CSS selector, text="...", role=..., placeholder="...", label="...", or plain text',
|
|
665
|
+
},
|
|
666
|
+
value: {
|
|
667
|
+
type: 'string',
|
|
668
|
+
description: 'Value for fill/type actions, URL for navigate, key for press-key, expression for evaluate, or tab index for switch-tab',
|
|
669
|
+
},
|
|
670
|
+
options: {
|
|
671
|
+
type: 'string',
|
|
672
|
+
description: 'Additional options as JSON string, e.g. \'{"timeout": 5000}\' or \'{"selector": ".class"}\'',
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
required: ['action'],
|
|
676
|
+
},
|
|
677
|
+
async execute(args) {
|
|
678
|
+
const action = args.action;
|
|
679
|
+
const target = args.target;
|
|
680
|
+
const value = args.value;
|
|
681
|
+
const options = args.options ? JSON.parse(args.options) : {};
|
|
682
|
+
const timeout = options.timeout || 15000;
|
|
683
|
+
try {
|
|
684
|
+
switch (action) {
|
|
685
|
+
case 'set-proxy': {
|
|
686
|
+
if (!value && !target)
|
|
687
|
+
return err('set-proxy requires a proxy address', 'Examples: "host:port", "user:pass@host:port", or "off" to disable');
|
|
688
|
+
const proxy = value || target || '';
|
|
689
|
+
if (proxy === 'off' || proxy === 'none' || proxy === '') {
|
|
690
|
+
browserProxy = '';
|
|
691
|
+
await closeBrowser();
|
|
692
|
+
return ok('Proxy disabled. Browser will restart on next action.');
|
|
693
|
+
}
|
|
694
|
+
browserProxy = proxy;
|
|
695
|
+
await closeBrowser();
|
|
696
|
+
return ok(`Proxy set to ${proxy}. Browser will restart with proxy on next action.`, {
|
|
697
|
+
proxy: proxy,
|
|
698
|
+
note: 'If proxy requires auth, use format: user:pass@host:port',
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
case 'set-ui': {
|
|
702
|
+
const mode = (value || target || '').toLowerCase();
|
|
703
|
+
if (mode === 'on' || mode === 'show' || mode === 'headed' || mode === 'visible' || mode === 'true') {
|
|
704
|
+
browserHeadless = false;
|
|
705
|
+
await closeBrowser();
|
|
706
|
+
return ok('Browser UI enabled (headed mode). Browser will restart on next action.', {
|
|
707
|
+
mode: 'headed — browser window will be visible',
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
else if (mode === 'off' || mode === 'hide' || mode === 'headless' || mode === 'false') {
|
|
711
|
+
browserHeadless = true;
|
|
712
|
+
await closeBrowser();
|
|
713
|
+
return ok('Browser UI disabled (headless mode). Browser will restart on next action.', {
|
|
714
|
+
mode: 'headless — browser runs in background',
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
return `Current mode: ${browserHeadless ? 'headless (hidden)' : 'headed (visible)'}\n\nUse: set-ui value="on" (show window) or set-ui value="off" (hide window)`;
|
|
718
|
+
}
|
|
719
|
+
case 'open-tabs': {
|
|
720
|
+
const p = await ensureBrowser();
|
|
721
|
+
const session = getSession();
|
|
722
|
+
const count = parseInt(value) || 3;
|
|
723
|
+
const urls = target ? target.split(',').map(s => s.trim()) : [];
|
|
724
|
+
const tabs = [];
|
|
725
|
+
for (let i = 0; i < count; i++) {
|
|
726
|
+
const newPage = await session.context.newPage();
|
|
727
|
+
if (urls[i]) {
|
|
728
|
+
const url = urls[i].startsWith('http') ? urls[i] : `https://${urls[i]}`;
|
|
729
|
+
await newPage.goto(url, { waitUntil: 'domcontentloaded', timeout }).catch(() => { });
|
|
730
|
+
tabs.push(`Tab ${i + 1}: ${newPage.url()} — ${await newPage.title().catch(() => '')}`);
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
tabs.push(`Tab ${i + 1}: blank`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return ok(`Opened ${count} tabs`, {
|
|
737
|
+
total: `${session.context.pages().length} tabs open`,
|
|
738
|
+
tabs: tabs.join('\n'),
|
|
739
|
+
hint: 'Use switch-tab to navigate between tabs, or run signup-assist/signin-assist on the current tab',
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
case 'status': {
|
|
743
|
+
const session = getSession();
|
|
744
|
+
if (!session || session.page.isClosed()) {
|
|
745
|
+
return `Browser: not running. Use action "navigate" to start it.\nProfile: ${BASE_PROFILE_DIR}\nEngine: Chromium\nMode: ${browserHeadless ? 'headless' : 'headed'}\nProxy: ${browserProxy || 'none'}`;
|
|
746
|
+
}
|
|
747
|
+
const title = await session.page.title();
|
|
748
|
+
return `Browser: running\nEngine: Chromium\nProfile: ${session.profileDir}\nMode: ${browserHeadless ? 'headless' : 'headed'}\nProxy: ${browserProxy || 'none'}\nURL: ${session.page.url()}\nTitle: ${title}\nOpen tabs: ${session.context.pages().length}`;
|
|
749
|
+
}
|
|
750
|
+
case 'close': {
|
|
751
|
+
const session = getSession();
|
|
752
|
+
const profilePath = session?.profileDir || BASE_PROFILE_DIR;
|
|
753
|
+
await closeBrowser();
|
|
754
|
+
return 'Browser closed. Profile preserved at ' + profilePath;
|
|
755
|
+
}
|
|
756
|
+
case 'navigate': {
|
|
757
|
+
const p = await ensureBrowser();
|
|
758
|
+
const url = value || target;
|
|
759
|
+
if (!url)
|
|
760
|
+
return 'Error: navigate requires a URL (use value or target parameter)';
|
|
761
|
+
const fullUrl = url.startsWith('http') ? url : `https://${url}`;
|
|
762
|
+
await p.goto(fullUrl, { waitUntil: 'domcontentloaded', timeout });
|
|
763
|
+
const title = await p.title();
|
|
764
|
+
return `Navigated to: ${p.url()}\nTitle: ${title}`;
|
|
765
|
+
}
|
|
766
|
+
case 'click': {
|
|
767
|
+
const p = await ensureBrowser();
|
|
768
|
+
if (!target)
|
|
769
|
+
return 'Error: click requires a target element';
|
|
770
|
+
const locator = await resolveLocator(p, target);
|
|
771
|
+
await locator.first().click({ timeout });
|
|
772
|
+
await p.waitForTimeout(500);
|
|
773
|
+
return `Clicked: ${target}\nURL: ${p.url()}\nTitle: ${await p.title()}`;
|
|
774
|
+
}
|
|
775
|
+
case 'fill': {
|
|
776
|
+
const p = await ensureBrowser();
|
|
777
|
+
if (!target)
|
|
778
|
+
return 'Error: fill requires a target element';
|
|
779
|
+
if (value === undefined)
|
|
780
|
+
return 'Error: fill requires a value';
|
|
781
|
+
const locator = await resolveLocator(p, target);
|
|
782
|
+
await locator.first().fill(value, { timeout });
|
|
783
|
+
return `Filled "${target}" with "${value.length > 50 ? value.slice(0, 50) + '...' : value}"`;
|
|
784
|
+
}
|
|
785
|
+
case 'type': {
|
|
786
|
+
const p = await ensureBrowser();
|
|
787
|
+
if (!target)
|
|
788
|
+
return 'Error: type requires a target element';
|
|
789
|
+
if (value === undefined)
|
|
790
|
+
return 'Error: type requires a value';
|
|
791
|
+
const locator = await resolveLocator(p, target);
|
|
792
|
+
await locator.first().pressSequentially(value, { delay: 50 });
|
|
793
|
+
return `Typed "${value.length > 50 ? value.slice(0, 50) + '...' : value}" into "${target}"`;
|
|
794
|
+
}
|
|
795
|
+
case 'press-key': {
|
|
796
|
+
const p = await ensureBrowser();
|
|
797
|
+
const key = value || target;
|
|
798
|
+
if (!key)
|
|
799
|
+
return 'Error: press-key requires a key (e.g. "Enter", "Tab", "Escape", "Control+a")';
|
|
800
|
+
await p.keyboard.press(key);
|
|
801
|
+
await p.waitForTimeout(300);
|
|
802
|
+
return `Pressed key: ${key}\nURL: ${p.url()}`;
|
|
803
|
+
}
|
|
804
|
+
case 'select': {
|
|
805
|
+
const p = await ensureBrowser();
|
|
806
|
+
if (!target)
|
|
807
|
+
return 'Error: select requires a target <select> element';
|
|
808
|
+
if (value === undefined)
|
|
809
|
+
return 'Error: select requires a value (option value)';
|
|
810
|
+
const locator = await resolveLocator(p, target);
|
|
811
|
+
await locator.first().selectOption(value, { timeout });
|
|
812
|
+
return `Selected "${value}" in "${target}"`;
|
|
813
|
+
}
|
|
814
|
+
case 'screenshot': {
|
|
815
|
+
const p = await ensureBrowser();
|
|
816
|
+
const screenshotPath = options.path || join(homedir(), '.aurix-screenshot.png');
|
|
817
|
+
if (target) {
|
|
818
|
+
const locator = await resolveLocator(p, target);
|
|
819
|
+
await locator.first().screenshot({ path: screenshotPath });
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
await p.screenshot({ path: screenshotPath, fullPage: !!options.fullPage });
|
|
823
|
+
}
|
|
824
|
+
return `Screenshot saved to: ${screenshotPath}`;
|
|
825
|
+
}
|
|
826
|
+
case 'snapshot': {
|
|
827
|
+
const p = await ensureBrowser();
|
|
828
|
+
const snapshot = await p.locator('body').evaluate((el) => {
|
|
829
|
+
function walk(node, depth) {
|
|
830
|
+
if (depth > 8)
|
|
831
|
+
return '';
|
|
832
|
+
const indent = ' '.repeat(depth);
|
|
833
|
+
const tag = node.tagName?.toLowerCase() || '';
|
|
834
|
+
const role = node.getAttribute('role') || '';
|
|
835
|
+
const ariaLabel = node.getAttribute('aria-label') || '';
|
|
836
|
+
const text = node.childNodes.length === 1 && node.childNodes[0].nodeType === 3
|
|
837
|
+
? node.childNodes[0].textContent?.trim().slice(0, 80) || ''
|
|
838
|
+
: '';
|
|
839
|
+
const href = node.getAttribute('href') || '';
|
|
840
|
+
const type = node.getAttribute('type') || '';
|
|
841
|
+
const name = node.getAttribute('name') || '';
|
|
842
|
+
const id = node.getAttribute('id') || '';
|
|
843
|
+
let line = `${indent}<${tag}`;
|
|
844
|
+
if (role)
|
|
845
|
+
line += ` role="${role}"`;
|
|
846
|
+
if (ariaLabel)
|
|
847
|
+
line += ` aria-label="${ariaLabel}"`;
|
|
848
|
+
if (href)
|
|
849
|
+
line += ` href="${href}"`;
|
|
850
|
+
if (type)
|
|
851
|
+
line += ` type="${type}"`;
|
|
852
|
+
if (name)
|
|
853
|
+
line += ` name="${name}"`;
|
|
854
|
+
if (id)
|
|
855
|
+
line += ` id="${id}"`;
|
|
856
|
+
line += '>';
|
|
857
|
+
if (text)
|
|
858
|
+
line += ` ${text}`;
|
|
859
|
+
line += '\n';
|
|
860
|
+
for (const child of Array.from(node.children)) {
|
|
861
|
+
line += walk(child, depth + 1);
|
|
862
|
+
}
|
|
863
|
+
return line;
|
|
864
|
+
}
|
|
865
|
+
return walk(el, 0);
|
|
866
|
+
});
|
|
867
|
+
return snapshot || '(empty page)';
|
|
868
|
+
}
|
|
869
|
+
case 'text': {
|
|
870
|
+
const p = await ensureBrowser();
|
|
871
|
+
if (target) {
|
|
872
|
+
const locator = await resolveLocator(p, target);
|
|
873
|
+
const text = await locator.first().textContent({ timeout });
|
|
874
|
+
return text || '(empty)';
|
|
875
|
+
}
|
|
876
|
+
const bodyText = await p.locator('body').innerText({ timeout: 5000 });
|
|
877
|
+
return bodyText.length > 10000
|
|
878
|
+
? bodyText.slice(0, 10000) + `\n\n... [${bodyText.length - 10000} more chars]`
|
|
879
|
+
: bodyText;
|
|
880
|
+
}
|
|
881
|
+
case 'html': {
|
|
882
|
+
const p = await ensureBrowser();
|
|
883
|
+
if (target) {
|
|
884
|
+
const locator = await resolveLocator(p, target);
|
|
885
|
+
const html = await locator.first().innerHTML({ timeout });
|
|
886
|
+
return html;
|
|
887
|
+
}
|
|
888
|
+
const html = await p.content();
|
|
889
|
+
return html.length > 15000
|
|
890
|
+
? html.slice(0, 15000) + `\n\n... [${html.length - 15000} more chars]`
|
|
891
|
+
: html;
|
|
892
|
+
}
|
|
893
|
+
case 'url': {
|
|
894
|
+
const p = await ensureBrowser();
|
|
895
|
+
return p.url();
|
|
896
|
+
}
|
|
897
|
+
case 'title': {
|
|
898
|
+
const p = await ensureBrowser();
|
|
899
|
+
return await p.title();
|
|
900
|
+
}
|
|
901
|
+
case 'scroll': {
|
|
902
|
+
const p = await ensureBrowser();
|
|
903
|
+
const direction = value || 'down';
|
|
904
|
+
const amount = options.amount || 500;
|
|
905
|
+
if (target) {
|
|
906
|
+
const locator = await resolveLocator(p, target);
|
|
907
|
+
await locator.first().scrollIntoViewIfNeeded({ timeout });
|
|
908
|
+
return `Scrolled to element: ${target}`;
|
|
909
|
+
}
|
|
910
|
+
const deltaY = direction === 'up' ? -amount : amount;
|
|
911
|
+
await p.mouse.wheel(0, deltaY);
|
|
912
|
+
await p.waitForTimeout(300);
|
|
913
|
+
return `Scrolled ${direction} by ${amount}px`;
|
|
914
|
+
}
|
|
915
|
+
case 'back': {
|
|
916
|
+
const p = await ensureBrowser();
|
|
917
|
+
await p.goBack({ waitUntil: 'domcontentloaded', timeout });
|
|
918
|
+
return `Navigated back\nURL: ${p.url()}\nTitle: ${await p.title()}`;
|
|
919
|
+
}
|
|
920
|
+
case 'forward': {
|
|
921
|
+
const p = await ensureBrowser();
|
|
922
|
+
await p.goForward({ waitUntil: 'domcontentloaded', timeout });
|
|
923
|
+
return `Navigated forward\nURL: ${p.url()}\nTitle: ${await p.title()}`;
|
|
924
|
+
}
|
|
925
|
+
case 'wait': {
|
|
926
|
+
const p = await ensureBrowser();
|
|
927
|
+
if (target) {
|
|
928
|
+
const locator = await resolveLocator(p, target);
|
|
929
|
+
await locator.first().waitFor({ state: options.state || 'visible', timeout });
|
|
930
|
+
return `Element appeared: ${target}`;
|
|
931
|
+
}
|
|
932
|
+
const ms = parseInt(value) || 2000;
|
|
933
|
+
await p.waitForTimeout(ms);
|
|
934
|
+
return `Waited ${ms}ms`;
|
|
935
|
+
}
|
|
936
|
+
case 'evaluate': {
|
|
937
|
+
const p = await ensureBrowser();
|
|
938
|
+
const code = value || target;
|
|
939
|
+
if (!code)
|
|
940
|
+
return 'Error: evaluate requires JavaScript code (use value parameter)';
|
|
941
|
+
if (consecutiveEvalFailures >= 2 && code === lastEvalCode) {
|
|
942
|
+
return `STOP. You've tried this exact evaluate code ${consecutiveEvalFailures} times and it failed every time.\n\n` +
|
|
943
|
+
`DO NOT use evaluate again. Use these actions instead:\n` +
|
|
944
|
+
` 1. snapshot — see all elements on the page (including inside iframes)\n` +
|
|
945
|
+
` 2. fill target="input[name='email'], #email, input[type='email']" value="..." — fill a field\n` +
|
|
946
|
+
` 3. click target="button[type='submit'], #submit, text=Next" — click a button\n` +
|
|
947
|
+
` 4. type target="<selector>" value="..." — type into a field\n\n` +
|
|
948
|
+
`These actions automatically search the main page AND all iframes. Start with "snapshot" to find the right selectors.`;
|
|
949
|
+
}
|
|
950
|
+
if (code !== lastEvalCode) {
|
|
951
|
+
consecutiveEvalFailures = 0;
|
|
952
|
+
lastEvalCode = '';
|
|
953
|
+
}
|
|
954
|
+
try {
|
|
955
|
+
const result = await p.evaluate(code);
|
|
956
|
+
consecutiveEvalFailures = 0;
|
|
957
|
+
lastEvalCode = '';
|
|
958
|
+
return typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
959
|
+
}
|
|
960
|
+
catch (evalErr) {
|
|
961
|
+
const errMsg = evalErr.message || String(evalErr);
|
|
962
|
+
if (errMsg.includes('null') || errMsg.includes('Cannot read propert') || errMsg.includes('Cannot set propert')) {
|
|
963
|
+
lastEvalCode = code;
|
|
964
|
+
consecutiveEvalFailures++;
|
|
965
|
+
for (const frame of p.frames()) {
|
|
966
|
+
if (frame === p.mainFrame())
|
|
967
|
+
continue;
|
|
968
|
+
try {
|
|
969
|
+
const r = await frame.evaluate(code);
|
|
970
|
+
consecutiveEvalFailures = 0;
|
|
971
|
+
lastEvalCode = '';
|
|
972
|
+
return typeof r === 'string' ? r : JSON.stringify(r, null, 2);
|
|
973
|
+
}
|
|
974
|
+
catch { }
|
|
975
|
+
}
|
|
976
|
+
if (consecutiveEvalFailures >= 2) {
|
|
977
|
+
return `STOP — evaluate has failed ${consecutiveEvalFailures} times with the same code. The element does not exist on this page or any iframe.\n\n` +
|
|
978
|
+
`You MUST switch to using click, fill, and type actions NOW. These actions auto-search all frames.\n` +
|
|
979
|
+
`First, run: action="snapshot" (no target needed) — this shows all elements.\n` +
|
|
980
|
+
`Then use the selectors from the snapshot with fill/click/type.`;
|
|
981
|
+
}
|
|
982
|
+
return `Error: Element not found on main page or iframes (attempt ${consecutiveEvalFailures}).\n\n` +
|
|
983
|
+
`Use these actions instead — they auto-search all frames including iframes:\n` +
|
|
984
|
+
` click target="<selector>"\n` +
|
|
985
|
+
` fill target="<selector>" value="<text>"\n` +
|
|
986
|
+
` type target="<selector>" value="<text>"\n\n` +
|
|
987
|
+
`Use "snapshot" first to find the correct selectors.`;
|
|
988
|
+
}
|
|
989
|
+
return `Browser error: ${errMsg.slice(0, 300)}`;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
case 'new-tab': {
|
|
993
|
+
const p = await ensureBrowser();
|
|
994
|
+
const session = getSession();
|
|
995
|
+
const newPage = await session.context.newPage();
|
|
996
|
+
if (value) {
|
|
997
|
+
const url = value.startsWith('http') ? value : `https://${value}`;
|
|
998
|
+
await newPage.goto(url, { waitUntil: 'domcontentloaded', timeout });
|
|
999
|
+
}
|
|
1000
|
+
session.page = newPage;
|
|
1001
|
+
sessions.set(currentSessionKey, session);
|
|
1002
|
+
return `New tab opened (${session.context.pages().length} tabs total)\nURL: ${newPage.url()}`;
|
|
1003
|
+
}
|
|
1004
|
+
case 'switch-tab': {
|
|
1005
|
+
const p = await ensureBrowser();
|
|
1006
|
+
const session = getSession();
|
|
1007
|
+
const pages = session.context.pages();
|
|
1008
|
+
const idx = parseInt(value) || 0;
|
|
1009
|
+
if (idx < 0 || idx >= pages.length)
|
|
1010
|
+
return `Error: tab index ${idx} out of range (0-${pages.length - 1})`;
|
|
1011
|
+
session.page = pages[idx];
|
|
1012
|
+
sessions.set(currentSessionKey, session);
|
|
1013
|
+
await session.page.bringToFront();
|
|
1014
|
+
return `Switched to tab ${idx}\nURL: ${session.page.url()}\nTitle: ${await session.page.title()}`;
|
|
1015
|
+
}
|
|
1016
|
+
case 'close-tab': {
|
|
1017
|
+
const p = await ensureBrowser();
|
|
1018
|
+
const session = getSession();
|
|
1019
|
+
const pages = session.context.pages();
|
|
1020
|
+
if (pages.length <= 1)
|
|
1021
|
+
return 'Cannot close the only tab. Use "close" to shut down the browser.';
|
|
1022
|
+
const idx = value ? parseInt(value) : pages.indexOf(p);
|
|
1023
|
+
const toClose = pages[idx];
|
|
1024
|
+
if (!toClose)
|
|
1025
|
+
return `Error: tab index ${idx} not found`;
|
|
1026
|
+
await toClose.close();
|
|
1027
|
+
session.page = session.context.pages()[0];
|
|
1028
|
+
sessions.set(currentSessionKey, session);
|
|
1029
|
+
return `Closed tab ${idx}. ${session.context.pages().length} tabs remaining.\nCurrent URL: ${session.page.url()}`;
|
|
1030
|
+
}
|
|
1031
|
+
case 'cookies': {
|
|
1032
|
+
const p = await ensureBrowser();
|
|
1033
|
+
const session = getSession();
|
|
1034
|
+
const cookies = await session.context.cookies(value ? [value] : undefined);
|
|
1035
|
+
if (cookies.length === 0)
|
|
1036
|
+
return 'No cookies found';
|
|
1037
|
+
return cookies.map(c => `${c.domain} | ${c.name}=${c.value.slice(0, 40)}${c.value.length > 40 ? '...' : ''}`).join('\n');
|
|
1038
|
+
}
|
|
1039
|
+
case 'upload': {
|
|
1040
|
+
const p = await ensureBrowser();
|
|
1041
|
+
if (!target)
|
|
1042
|
+
return 'Error: upload requires target (file input element)';
|
|
1043
|
+
if (!value)
|
|
1044
|
+
return 'Error: upload requires value (file path)';
|
|
1045
|
+
const locator = await resolveLocator(p, target);
|
|
1046
|
+
await locator.first().setInputFiles(value, { timeout });
|
|
1047
|
+
return `Uploaded file: ${value}`;
|
|
1048
|
+
}
|
|
1049
|
+
case 'detect-captcha': {
|
|
1050
|
+
const p = await ensureBrowser();
|
|
1051
|
+
const frames = p.frames();
|
|
1052
|
+
const captchaInfo = [];
|
|
1053
|
+
for (const frame of frames) {
|
|
1054
|
+
const url = frame.url();
|
|
1055
|
+
if (url.includes('recaptcha') || url.includes('google.com/recaptcha')) {
|
|
1056
|
+
captchaInfo.push(`reCAPTCHA iframe: ${url.slice(0, 100)}`);
|
|
1057
|
+
}
|
|
1058
|
+
if (url.includes('hcaptcha') || url.includes('newassets.hcaptcha')) {
|
|
1059
|
+
captchaInfo.push(`hCaptcha iframe: ${url.slice(0, 100)}`);
|
|
1060
|
+
}
|
|
1061
|
+
if (url.includes('funcaptcha') || url.includes('arkoselabs')) {
|
|
1062
|
+
captchaInfo.push(`FunCaptcha iframe: ${url.slice(0, 100)}`);
|
|
1063
|
+
}
|
|
1064
|
+
if (url.includes('geetest') || url.includes('captcha.com')) {
|
|
1065
|
+
captchaInfo.push(`GeeTest iframe: ${url.slice(0, 100)}`);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
const pageContent = await p.content();
|
|
1069
|
+
if (pageContent.includes('g-recaptcha') || pageContent.includes('recaptcha'))
|
|
1070
|
+
captchaInfo.push('reCAPTCHA element detected in DOM');
|
|
1071
|
+
if (pageContent.includes('h-captcha') || pageContent.includes('hcaptcha'))
|
|
1072
|
+
captchaInfo.push('hCaptcha element detected in DOM');
|
|
1073
|
+
if (pageContent.includes('cf-turnstile') || pageContent.includes('challenges.cloudflare'))
|
|
1074
|
+
captchaInfo.push('Cloudflare Turnstile detected');
|
|
1075
|
+
if (pageContent.includes('captcha-image') || pageContent.includes('captcha_img'))
|
|
1076
|
+
captchaInfo.push('Image captcha detected (may need manual solving)');
|
|
1077
|
+
if (captchaInfo.length === 0)
|
|
1078
|
+
return 'No captcha detected on this page.';
|
|
1079
|
+
return `Captcha detected:\n${captchaInfo.map(c => ` - ${c}`).join('\n')}\n\nUse action "solve-captcha" to attempt solving.`;
|
|
1080
|
+
}
|
|
1081
|
+
case 'solve-captcha': {
|
|
1082
|
+
const p = await ensureBrowser();
|
|
1083
|
+
const results = [];
|
|
1084
|
+
const frames = p.frames();
|
|
1085
|
+
let captchaType = 'unknown';
|
|
1086
|
+
let recaptchaAnchor = null;
|
|
1087
|
+
let recaptchaBframe = null;
|
|
1088
|
+
let hcaptchaCheckbox = null;
|
|
1089
|
+
let hcaptchaChallenge = null;
|
|
1090
|
+
let funcaptchaFrame = null;
|
|
1091
|
+
let mtcaptchaFrame = null;
|
|
1092
|
+
let geetestFrame = null;
|
|
1093
|
+
for (const frame of frames) {
|
|
1094
|
+
const url = frame.url();
|
|
1095
|
+
if (url.includes('/recaptcha/') && url.includes('/anchor'))
|
|
1096
|
+
recaptchaAnchor = frame;
|
|
1097
|
+
if (url.includes('/recaptcha/') && url.includes('/bframe'))
|
|
1098
|
+
recaptchaBframe = frame;
|
|
1099
|
+
if (url.includes('newassets.hcaptcha.com') && !url.includes('challenge'))
|
|
1100
|
+
hcaptchaCheckbox = frame;
|
|
1101
|
+
if (url.includes('hcaptcha') && url.includes('challenge'))
|
|
1102
|
+
hcaptchaChallenge = frame;
|
|
1103
|
+
if (url.includes('funcaptcha') || url.includes('arkoselabs'))
|
|
1104
|
+
funcaptchaFrame = frame;
|
|
1105
|
+
if (url.includes('service.mtcaptcha'))
|
|
1106
|
+
mtcaptchaFrame = frame;
|
|
1107
|
+
if ((url.includes('geetest.com') || url.includes('captcha.com')) && !url.includes('recaptcha') && !url.includes('hcaptcha'))
|
|
1108
|
+
geetestFrame = frame;
|
|
1109
|
+
}
|
|
1110
|
+
if (recaptchaAnchor)
|
|
1111
|
+
captchaType = 'recaptcha';
|
|
1112
|
+
else if (hcaptchaCheckbox)
|
|
1113
|
+
captchaType = 'hcaptcha';
|
|
1114
|
+
else if (funcaptchaFrame)
|
|
1115
|
+
captchaType = 'funcaptcha';
|
|
1116
|
+
else if (mtcaptchaFrame)
|
|
1117
|
+
captchaType = 'mtcaptcha';
|
|
1118
|
+
else if (geetestFrame)
|
|
1119
|
+
captchaType = 'geetest';
|
|
1120
|
+
const pageContent = await p.content();
|
|
1121
|
+
if (captchaType === 'unknown' && (pageContent.includes('cf-turnstile') || pageContent.includes('challenges.cloudflare'))) {
|
|
1122
|
+
captchaType = 'turnstile';
|
|
1123
|
+
}
|
|
1124
|
+
if (captchaType === 'unknown' && (pageContent.includes('mtcaptcha') || pageContent.includes('MTCaptcha'))) {
|
|
1125
|
+
captchaType = 'mtcaptcha';
|
|
1126
|
+
}
|
|
1127
|
+
if (captchaType === 'unknown' && recaptchaBframe)
|
|
1128
|
+
captchaType = 'recaptcha';
|
|
1129
|
+
if (captchaType === 'recaptcha') {
|
|
1130
|
+
results.push('Attempting reCAPTCHA...');
|
|
1131
|
+
try {
|
|
1132
|
+
let checkboxFrame = recaptchaAnchor;
|
|
1133
|
+
if (!checkboxFrame) {
|
|
1134
|
+
for (const frame of frames) {
|
|
1135
|
+
const url = frame.url();
|
|
1136
|
+
if (url.includes('recaptcha') && (url.includes('anchor') || url.includes('api2/'))) {
|
|
1137
|
+
checkboxFrame = frame;
|
|
1138
|
+
break;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
if (checkboxFrame) {
|
|
1143
|
+
const checkbox = checkboxFrame.locator('#recaptcha-anchor, .recaptcha-checkbox, .rc-anchor-checkbox');
|
|
1144
|
+
if (await checkbox.count() > 0) {
|
|
1145
|
+
await p.waitForTimeout(1000 + Math.random() * 1500);
|
|
1146
|
+
await checkbox.first().click({ force: true });
|
|
1147
|
+
await p.waitForTimeout(3000);
|
|
1148
|
+
const updatedFrames = p.frames();
|
|
1149
|
+
const challengeFrame = updatedFrames.find(f => f.url().includes('/recaptcha/') && f.url().includes('/bframe'));
|
|
1150
|
+
if (challengeFrame) {
|
|
1151
|
+
results.push('Image challenge appeared. Analyzing grid...');
|
|
1152
|
+
const gridResult = await analyzeImageChallenge(p, challengeFrame, 'recaptcha');
|
|
1153
|
+
results.push(gridResult);
|
|
1154
|
+
}
|
|
1155
|
+
else {
|
|
1156
|
+
const checkmark = checkboxFrame.locator('.recaptcha-checkbox-checked, .rc-anchor-checkbox-checked');
|
|
1157
|
+
if (await checkmark.count() > 0) {
|
|
1158
|
+
results.push(ok('reCAPTCHA solved — no image challenge needed', { status: 'verified' }));
|
|
1159
|
+
}
|
|
1160
|
+
else {
|
|
1161
|
+
results.push(warn('Checkbox clicked but verification unclear', { suggestion: 'Use "captcha-grid" to check for image challenge' }));
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
else {
|
|
1166
|
+
results.push(warn('reCAPTCHA anchor frame found but checkbox element missing', { action: 'trying click on anchor body' }));
|
|
1167
|
+
await checkboxFrame.locator('#recaptcha-anchor').click({ force: true }).catch(() => { });
|
|
1168
|
+
await p.waitForTimeout(3000);
|
|
1169
|
+
results.push('Use "captcha-grid" to check for image challenge.');
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
results.push(warn('No reCAPTCHA anchor frame found', { action: 'trying main page widget' }));
|
|
1174
|
+
const mainCheckbox = p.locator('.g-recaptcha, [data-sitekey]');
|
|
1175
|
+
if (await mainCheckbox.count() > 0) {
|
|
1176
|
+
await mainCheckbox.first().click({ force: true });
|
|
1177
|
+
await p.waitForTimeout(3000);
|
|
1178
|
+
results.push('Clicked reCAPTCHA widget. Use "captcha-grid" if image challenge appeared.');
|
|
1179
|
+
}
|
|
1180
|
+
else {
|
|
1181
|
+
results.push(err('reCAPTCHA widget not found on page', 'Check if the page has loaded or use "detect-captcha" first'));
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
catch (e) {
|
|
1186
|
+
results.push(err(`reCAPTCHA click failed: ${e.message}`, 'Use "detect-captcha" first, then retry "solve-captcha"'));
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
if (captchaType === 'hcaptcha') {
|
|
1190
|
+
results.push('Attempting hCaptcha...');
|
|
1191
|
+
try {
|
|
1192
|
+
const checkboxFrame = hcaptchaCheckbox;
|
|
1193
|
+
if (checkboxFrame) {
|
|
1194
|
+
const checkbox = checkboxFrame.locator('#checkbox, .check');
|
|
1195
|
+
if (await checkbox.count() > 0) {
|
|
1196
|
+
await p.waitForTimeout(800 + Math.random() * 1200);
|
|
1197
|
+
await checkbox.first().click({ force: true });
|
|
1198
|
+
await p.waitForTimeout(3000);
|
|
1199
|
+
const updatedFrames = p.frames();
|
|
1200
|
+
const challengeFrame = updatedFrames.find((f) => f.url().includes('hcaptcha') && f.url().includes('challenge'));
|
|
1201
|
+
if (challengeFrame) {
|
|
1202
|
+
results.push('Image challenge appeared. Analyzing grid...');
|
|
1203
|
+
const gridResult = await analyzeImageChallenge(p, challengeFrame, 'hcaptcha');
|
|
1204
|
+
results.push(gridResult);
|
|
1205
|
+
}
|
|
1206
|
+
else {
|
|
1207
|
+
const checkmark = checkboxFrame.locator('.check.solved, #checkbox[aria-checked="true"]');
|
|
1208
|
+
if (await checkmark.count() > 0) {
|
|
1209
|
+
results.push(ok('hCaptcha solved', { status: 'verified' }));
|
|
1210
|
+
}
|
|
1211
|
+
else {
|
|
1212
|
+
results.push(warn('hCaptcha checkbox clicked, status unclear', { suggestion: 'Use "captcha-grid" to check for image challenge' }));
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
else {
|
|
1217
|
+
results.push(err('hCaptcha checkbox element not found in frame'));
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1221
|
+
results.push(err('hCaptcha checkbox frame not found', 'Use "detect-captcha" to scan for captcha type'));
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
catch (e) {
|
|
1225
|
+
results.push(err(`hCaptcha click failed: ${e.message}`));
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
if (captchaType === 'turnstile') {
|
|
1229
|
+
results.push('Attempting Cloudflare Turnstile...');
|
|
1230
|
+
try {
|
|
1231
|
+
const turnstileFrame = frames.find(f => f.url().includes('challenges.cloudflare'));
|
|
1232
|
+
if (turnstileFrame) {
|
|
1233
|
+
await p.waitForTimeout(1500 + Math.random() * 1000);
|
|
1234
|
+
const cb = turnstileFrame.locator('input[type="checkbox"], .cb-lb');
|
|
1235
|
+
if (await cb.count() > 0) {
|
|
1236
|
+
await cb.first().click({ force: true });
|
|
1237
|
+
await p.waitForTimeout(3000);
|
|
1238
|
+
results.push(ok('Turnstile checkbox clicked'));
|
|
1239
|
+
}
|
|
1240
|
+
else {
|
|
1241
|
+
await turnstileFrame.locator('body').click();
|
|
1242
|
+
await p.waitForTimeout(3000);
|
|
1243
|
+
results.push(warn('Turnstile frame clicked (managed challenge)', { next: 'Check if challenge resolved with screenshot' }));
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
catch (e) {
|
|
1248
|
+
results.push(err(`Turnstile failed: ${e.message}`));
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
if (captchaType === 'funcaptcha') {
|
|
1252
|
+
results.push('FunCaptcha (Arkose Labs) detected. Analyzing puzzle...');
|
|
1253
|
+
try {
|
|
1254
|
+
const fcFrame = funcaptchaFrame;
|
|
1255
|
+
if (fcFrame) {
|
|
1256
|
+
await p.waitForTimeout(2000);
|
|
1257
|
+
const puzzleType = await fcFrame.evaluate(() => {
|
|
1258
|
+
const body = document.body.innerHTML;
|
|
1259
|
+
if (body.includes('rotate') || body.includes('rotation'))
|
|
1260
|
+
return 'rotation';
|
|
1261
|
+
if (body.includes('pick') || body.includes('match'))
|
|
1262
|
+
return 'image-match';
|
|
1263
|
+
if (body.includes('drag') || body.includes('drop'))
|
|
1264
|
+
return 'drag-drop';
|
|
1265
|
+
if (body.includes('count') || body.includes('how many'))
|
|
1266
|
+
return 'counting';
|
|
1267
|
+
if (body.includes('dice'))
|
|
1268
|
+
return 'dice';
|
|
1269
|
+
if (body.includes('gamemode') || body.includes('game'))
|
|
1270
|
+
return 'game';
|
|
1271
|
+
return 'unknown';
|
|
1272
|
+
}).catch(() => 'unknown');
|
|
1273
|
+
const instruction = await fcFrame.evaluate(() => {
|
|
1274
|
+
const h2 = document.querySelector('h2, h3, .challenge-title, #challenge-stage .title, [class*="instruction"], [class*="prompt"]');
|
|
1275
|
+
return h2?.textContent?.trim() || '';
|
|
1276
|
+
}).catch(() => '');
|
|
1277
|
+
results.push(`Puzzle type: ${puzzleType}`);
|
|
1278
|
+
if (instruction)
|
|
1279
|
+
results.push(`Instruction: "${instruction}"`);
|
|
1280
|
+
const screenshotPath = join(homedir(), '.aurix-funcaptcha-puzzle.png');
|
|
1281
|
+
try {
|
|
1282
|
+
await fcFrame.locator('#challenge-stage, .challenge-content, .game-content, body').first().screenshot({ path: screenshotPath });
|
|
1283
|
+
}
|
|
1284
|
+
catch {
|
|
1285
|
+
await p.screenshot({ path: screenshotPath });
|
|
1286
|
+
}
|
|
1287
|
+
results.push(`Puzzle screenshot: ${screenshotPath}`);
|
|
1288
|
+
const interactiveEls = await fcFrame.evaluate(() => {
|
|
1289
|
+
const els = [];
|
|
1290
|
+
document.querySelectorAll('canvas, img, [class*="game"], [class*="challenge"], [class*="puzzle"], button, input[type="range"], .slider').forEach(el => {
|
|
1291
|
+
els.push(`${el.tagName.toLowerCase()}.${el.className?.toString().slice(0, 60) || ''} [${el.getAttribute('role') || ''}]`);
|
|
1292
|
+
});
|
|
1293
|
+
return els;
|
|
1294
|
+
}).catch(() => []);
|
|
1295
|
+
if (interactiveEls.length > 0) {
|
|
1296
|
+
results.push(`Interactive elements: ${interactiveEls.slice(0, 10).join(', ')}`);
|
|
1297
|
+
}
|
|
1298
|
+
results.push('');
|
|
1299
|
+
results.push('To solve FunCaptcha:');
|
|
1300
|
+
results.push('1. Read the puzzle screenshot to understand the challenge');
|
|
1301
|
+
results.push('2. For rotation puzzles: use "drag-to" to rotate the object to the correct position');
|
|
1302
|
+
results.push('3. For image match: use "click" on matching images');
|
|
1303
|
+
results.push('4. For drag-drop: use "drag-to" with source and target coordinates');
|
|
1304
|
+
results.push('5. Use "evaluate" with JavaScript if puzzle needs programmatic interaction');
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
results.push(err('FunCaptcha frame not found', 'Use "detect-captcha" to scan the page first'));
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
catch (e) {
|
|
1311
|
+
results.push(err(`FunCaptcha analysis failed: ${e.message}`));
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
if (captchaType === 'mtcaptcha' || captchaType === 'geetest') {
|
|
1315
|
+
results.push(`Detected ${captchaType} challenge. Analyzing...`);
|
|
1316
|
+
const targetFrame = mtcaptchaFrame || geetestFrame || p;
|
|
1317
|
+
const hasSlider = await targetFrame.locator('.geetest_slider_button, .geetest_slider, [class*="slider_button"], [class*="slider-track"]').count();
|
|
1318
|
+
if (hasSlider > 0) {
|
|
1319
|
+
results.push('Type: SLIDER puzzle');
|
|
1320
|
+
results.push('The puzzle requires dragging a piece to fill a gap.');
|
|
1321
|
+
results.push('');
|
|
1322
|
+
const sliderInfo = await targetFrame.evaluate(() => {
|
|
1323
|
+
const info = {};
|
|
1324
|
+
const cut = document.querySelector('.geetest_cut, .geetest_piece_bg, [class*="geetest_cut"], [class*="slider_cut"], [class*="puzzle-gap"]');
|
|
1325
|
+
if (cut) {
|
|
1326
|
+
const cutRect = cut.getBoundingClientRect();
|
|
1327
|
+
const style = window.getComputedStyle(cut);
|
|
1328
|
+
info.cut = { left: cutRect.left, width: cutRect.width, styleLeft: parseFloat(style.left) || null, transform: style.transform || null };
|
|
1329
|
+
}
|
|
1330
|
+
const bg = document.querySelector('.geetest_canvas_bg, .geetest_bg, [class*="geetest_canvas"], canvas[class*="bg"]');
|
|
1331
|
+
if (bg) {
|
|
1332
|
+
const bgRect = bg.getBoundingClientRect();
|
|
1333
|
+
info.bg = { left: bgRect.left, width: bgRect.width };
|
|
1334
|
+
}
|
|
1335
|
+
const piece = document.querySelector('.geetest_piece, .geetest_slider_piece, [class*="slider_piece"]');
|
|
1336
|
+
if (piece) {
|
|
1337
|
+
const pieceRect = piece.getBoundingClientRect();
|
|
1338
|
+
info.piece = { left: pieceRect.left, width: pieceRect.width };
|
|
1339
|
+
}
|
|
1340
|
+
const slider = document.querySelector('.geetest_slider_button, .geetest_slider_knob, [class*="slider_button"]');
|
|
1341
|
+
if (slider) {
|
|
1342
|
+
const sliderRect = slider.getBoundingClientRect();
|
|
1343
|
+
info.slider = { left: sliderRect.left, width: sliderRect.width };
|
|
1344
|
+
}
|
|
1345
|
+
const track = document.querySelector('.geetest_slider_track, .geetest_slider, [class*="slider_track"]');
|
|
1346
|
+
if (track) {
|
|
1347
|
+
info.track = { width: track.getBoundingClientRect().width };
|
|
1348
|
+
}
|
|
1349
|
+
return info;
|
|
1350
|
+
});
|
|
1351
|
+
const puzzleEl = targetFrame.locator('.geetest_panel, .geetest_widget, [class*="geetest_container"]').first();
|
|
1352
|
+
const screenshotPath = join(homedir(), '.aurix-slider-puzzle.png');
|
|
1353
|
+
try {
|
|
1354
|
+
if (await puzzleEl.count() > 0)
|
|
1355
|
+
await puzzleEl.screenshot({ path: screenshotPath });
|
|
1356
|
+
else
|
|
1357
|
+
await p.screenshot({ path: screenshotPath });
|
|
1358
|
+
}
|
|
1359
|
+
catch {
|
|
1360
|
+
await p.screenshot({ path: screenshotPath });
|
|
1361
|
+
}
|
|
1362
|
+
results.push(`Puzzle screenshot: ${screenshotPath}`);
|
|
1363
|
+
let gapOffset = null;
|
|
1364
|
+
if (sliderInfo.cut && sliderInfo.bg) {
|
|
1365
|
+
if (sliderInfo.cut.styleLeft && sliderInfo.cut.styleLeft > 0) {
|
|
1366
|
+
gapOffset = Math.round(sliderInfo.cut.styleLeft);
|
|
1367
|
+
results.push(`Gap position (CSS left): ${gapOffset}px from puzzle left edge`);
|
|
1368
|
+
}
|
|
1369
|
+
else {
|
|
1370
|
+
gapOffset = Math.round(sliderInfo.cut.left - sliderInfo.bg.left);
|
|
1371
|
+
results.push(`Gap position (rect): ${gapOffset}px from puzzle left edge`);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
if (gapOffset === null && sliderInfo.cut?.transform && sliderInfo.cut.transform !== 'none') {
|
|
1375
|
+
const match = sliderInfo.cut.transform.match(/matrix\(.*?,\s*([\d.]+)/);
|
|
1376
|
+
if (match) {
|
|
1377
|
+
gapOffset = Math.round(parseFloat(match[1]));
|
|
1378
|
+
results.push(`Gap position (transform): ${gapOffset}px from puzzle left edge`);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (sliderInfo.slider)
|
|
1382
|
+
results.push(`Slider handle: x=${Math.round(sliderInfo.slider.left)}, width=${Math.round(sliderInfo.slider.width)}`);
|
|
1383
|
+
if (sliderInfo.track)
|
|
1384
|
+
results.push(`Track width: ${Math.round(sliderInfo.track.width)}px`);
|
|
1385
|
+
if (gapOffset !== null) {
|
|
1386
|
+
const pieceHalf = Math.round((sliderInfo.piece?.width || 44) / 2);
|
|
1387
|
+
const adjusted = gapOffset - pieceHalf;
|
|
1388
|
+
results.push('');
|
|
1389
|
+
results.push(`[OK] RECOMMENDED: drag-to target=".geetest_slider_button" value="${adjusted},0"`);
|
|
1390
|
+
results.push(`(gap ${gapOffset}px - half piece ${pieceHalf}px = ${adjusted}px drag distance)`);
|
|
1391
|
+
}
|
|
1392
|
+
else {
|
|
1393
|
+
results.push('');
|
|
1394
|
+
results.push('[WARN] Could not auto-detect gap. Look at the puzzle screenshot, find the gap/hole, and estimate the pixel offset.');
|
|
1395
|
+
results.push('Then: drag-to target=".geetest_slider_button" value="<estimated_px>,0"');
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
else {
|
|
1399
|
+
results.push('Type: IMAGE challenge');
|
|
1400
|
+
const gridResult = await analyzeImageChallenge(p, targetFrame, captchaType);
|
|
1401
|
+
results.push(gridResult);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
if (captchaType === 'unknown') {
|
|
1405
|
+
const imgCaptcha = p.locator('img[src*="captcha"], #captcha-image, .captcha-image, img.captcha');
|
|
1406
|
+
if (await imgCaptcha.count() > 0) {
|
|
1407
|
+
results.push('Text-based captcha detected.');
|
|
1408
|
+
const screenshotPath = join(homedir(), '.aurix-captcha-challenge.png');
|
|
1409
|
+
await imgCaptcha.first().screenshot({ path: screenshotPath });
|
|
1410
|
+
results.push(`Captcha image saved: ${screenshotPath}`);
|
|
1411
|
+
results.push('Read the text from the screenshot and use "fill" to type it into the captcha input field.');
|
|
1412
|
+
const input = p.locator('input[name*="captcha"], input[id*="captcha"], input[placeholder*="captcha" i], input[placeholder*="code" i]');
|
|
1413
|
+
if (await input.count() > 0) {
|
|
1414
|
+
const name = await input.first().getAttribute('name') || await input.first().getAttribute('id') || 'captcha input';
|
|
1415
|
+
results.push(`Captcha input field found: ${name}`);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
results.push('No recognizable captcha. Taking screenshot and scanning...');
|
|
1420
|
+
const screenshotPath = join(homedir(), '.aurix-captcha-challenge.png');
|
|
1421
|
+
await p.screenshot({ path: screenshotPath });
|
|
1422
|
+
results.push(`Screenshot saved: ${screenshotPath}`);
|
|
1423
|
+
results.push('Use "captcha-grid" to scan for any challenge overlay.');
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
const screenshotPath = join(homedir(), '.aurix-captcha-after.png');
|
|
1427
|
+
await p.screenshot({ path: screenshotPath });
|
|
1428
|
+
results.push(`\nPost-attempt screenshot: ${screenshotPath}`);
|
|
1429
|
+
return results.join('\n');
|
|
1430
|
+
}
|
|
1431
|
+
case 'captcha-grid': {
|
|
1432
|
+
const p = await ensureBrowser();
|
|
1433
|
+
const frames = p.frames();
|
|
1434
|
+
let challengeFrame = null;
|
|
1435
|
+
let provider = 'unknown';
|
|
1436
|
+
for (const frame of frames) {
|
|
1437
|
+
const url = frame.url();
|
|
1438
|
+
if (url.includes('bframe') || url.includes('recaptcha')) {
|
|
1439
|
+
challengeFrame = frame;
|
|
1440
|
+
provider = 'recaptcha';
|
|
1441
|
+
break;
|
|
1442
|
+
}
|
|
1443
|
+
if (url.includes('hcaptcha') && url.includes('challenge')) {
|
|
1444
|
+
challengeFrame = frame;
|
|
1445
|
+
provider = 'hcaptcha';
|
|
1446
|
+
break;
|
|
1447
|
+
}
|
|
1448
|
+
if (url.includes('mtcaptcha') || url.includes('service.mtcaptcha')) {
|
|
1449
|
+
challengeFrame = frame;
|
|
1450
|
+
provider = 'mtcaptcha';
|
|
1451
|
+
break;
|
|
1452
|
+
}
|
|
1453
|
+
if (url.includes('geetest') || url.includes('captcha.com')) {
|
|
1454
|
+
challengeFrame = frame;
|
|
1455
|
+
provider = 'geetest';
|
|
1456
|
+
break;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
if (!challengeFrame) {
|
|
1460
|
+
challengeFrame = p;
|
|
1461
|
+
const content = await p.content();
|
|
1462
|
+
if (content.includes('mtcaptcha'))
|
|
1463
|
+
provider = 'mtcaptcha';
|
|
1464
|
+
else if (content.includes('geetest'))
|
|
1465
|
+
provider = 'geetest';
|
|
1466
|
+
else
|
|
1467
|
+
provider = 'unknown';
|
|
1468
|
+
}
|
|
1469
|
+
const result = await analyzeImageChallenge(p, challengeFrame, provider);
|
|
1470
|
+
return result;
|
|
1471
|
+
}
|
|
1472
|
+
case 'click-tile': {
|
|
1473
|
+
const p = await ensureBrowser();
|
|
1474
|
+
const tileIndex = parseInt(value || target || '0');
|
|
1475
|
+
const frames = p.frames();
|
|
1476
|
+
let challengeFrame = null;
|
|
1477
|
+
let provider = 'unknown';
|
|
1478
|
+
for (const frame of frames) {
|
|
1479
|
+
const url = frame.url();
|
|
1480
|
+
if (url.includes('bframe') || url.includes('recaptcha')) {
|
|
1481
|
+
challengeFrame = frame;
|
|
1482
|
+
provider = 'recaptcha';
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
if (url.includes('hcaptcha') && url.includes('challenge')) {
|
|
1486
|
+
challengeFrame = frame;
|
|
1487
|
+
provider = 'hcaptcha';
|
|
1488
|
+
break;
|
|
1489
|
+
}
|
|
1490
|
+
if (url.includes('mtcaptcha') || url.includes('service.mtcaptcha')) {
|
|
1491
|
+
challengeFrame = frame;
|
|
1492
|
+
provider = 'mtcaptcha';
|
|
1493
|
+
break;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
if (!challengeFrame)
|
|
1497
|
+
challengeFrame = p;
|
|
1498
|
+
const tiles = await findGridTiles(challengeFrame, provider);
|
|
1499
|
+
if (tiles.length === 0)
|
|
1500
|
+
return err('No grid tiles found', 'Use "captcha-grid" to scan the challenge first');
|
|
1501
|
+
if (tileIndex < 0 || tileIndex >= tiles.length)
|
|
1502
|
+
return err(`Tile index ${tileIndex} out of range (0-${tiles.length - 1})`);
|
|
1503
|
+
try {
|
|
1504
|
+
const tile = tiles[tileIndex];
|
|
1505
|
+
await tile.click({ force: true });
|
|
1506
|
+
await p.waitForTimeout(500 + Math.random() * 400);
|
|
1507
|
+
const isRecaptcha = provider === 'recaptcha';
|
|
1508
|
+
const selectedClass = isRecaptcha ? '.rc-imageselect-dynamic-selected' : '.task-image.selected, .task .selected';
|
|
1509
|
+
const selectedCount = await challengeFrame.locator(selectedClass).count();
|
|
1510
|
+
if (isRecaptcha) {
|
|
1511
|
+
await p.waitForTimeout(1500 + Math.random() * 1000);
|
|
1512
|
+
const newTiles = await findGridTiles(challengeFrame, provider);
|
|
1513
|
+
const screenshotPath = join(homedir(), `.aurix-tile-after-${tileIndex}.png`);
|
|
1514
|
+
await challengeFrame.locator('.rc-imageselect-table-33, .rc-imageselect-table-44, table').first().screenshot({ path: screenshotPath }).catch(() => p.screenshot({ path: screenshotPath }));
|
|
1515
|
+
return ok(`Clicked tile ${tileIndex}`, {
|
|
1516
|
+
selected: `${selectedCount} tile(s)`,
|
|
1517
|
+
'new tile': 'appeared — check screenshot and evaluate',
|
|
1518
|
+
screenshot: screenshotPath,
|
|
1519
|
+
next: 'Use "click-tile" for next matching tile, or "captcha-verify" when done',
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
return ok(`Clicked tile ${tileIndex}`, {
|
|
1523
|
+
selected: `${selectedCount} tile(s)`,
|
|
1524
|
+
next: 'Continue clicking matching tiles, then use "captcha-verify"',
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
catch (e) {
|
|
1528
|
+
return err(`Failed to click tile ${tileIndex}: ${e.message}`, 'Use "captcha-grid" to re-scan the challenge');
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
case 'captcha-verify': {
|
|
1532
|
+
const p = await ensureBrowser();
|
|
1533
|
+
const frames = p.frames();
|
|
1534
|
+
let challengeFrame = null;
|
|
1535
|
+
let provider = 'unknown';
|
|
1536
|
+
for (const frame of frames) {
|
|
1537
|
+
const url = frame.url();
|
|
1538
|
+
if (url.includes('bframe') || url.includes('recaptcha')) {
|
|
1539
|
+
challengeFrame = frame;
|
|
1540
|
+
provider = 'recaptcha';
|
|
1541
|
+
break;
|
|
1542
|
+
}
|
|
1543
|
+
if (url.includes('hcaptcha') && url.includes('challenge')) {
|
|
1544
|
+
challengeFrame = frame;
|
|
1545
|
+
provider = 'hcaptcha';
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
if (url.includes('mtcaptcha') || url.includes('service.mtcaptcha')) {
|
|
1549
|
+
challengeFrame = frame;
|
|
1550
|
+
provider = 'mtcaptcha';
|
|
1551
|
+
break;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
if (!challengeFrame)
|
|
1555
|
+
challengeFrame = p;
|
|
1556
|
+
try {
|
|
1557
|
+
let verifyBtn = challengeFrame.locator('#recaptcha-verify-button, .rc-button-submit, .button-submit, [id*="verify"]');
|
|
1558
|
+
if (await verifyBtn.count() === 0) {
|
|
1559
|
+
verifyBtn = challengeFrame.locator('button:has-text("Verify"), button:has-text("Next"), button:has-text("Submit")');
|
|
1560
|
+
}
|
|
1561
|
+
if (await verifyBtn.count() === 0) {
|
|
1562
|
+
return err('No verify button found', 'Use "captcha-grid" to analyze the challenge first');
|
|
1563
|
+
}
|
|
1564
|
+
await verifyBtn.first().click({ force: true });
|
|
1565
|
+
await p.waitForTimeout(3000);
|
|
1566
|
+
const screenshotPath = join(homedir(), '.aurix-captcha-verify-result.png');
|
|
1567
|
+
const errorText = await challengeFrame.locator('.rc-imageselect-incorrect-response, .error-message, .incorrect').count();
|
|
1568
|
+
if (errorText > 0) {
|
|
1569
|
+
const errorMsg = await challengeFrame.locator('.rc-imageselect-incorrect-response, .error-message').first().textContent().catch(() => 'Incorrect answer');
|
|
1570
|
+
await p.screenshot({ path: screenshotPath });
|
|
1571
|
+
return err(`Verification failed: "${errorMsg}"`, `Challenge refreshed. Use "captcha-grid" to re-analyze, then click matching tiles again. Screenshot: ${screenshotPath}`);
|
|
1572
|
+
}
|
|
1573
|
+
const newChallenge = await challengeFrame.locator('.rc-imageselect-instructions, .prompt-text').count();
|
|
1574
|
+
if (newChallenge > 0) {
|
|
1575
|
+
const instruction = await challengeFrame.locator('.rc-imageselect-instructions, .prompt-text').first().textContent().catch(() => '');
|
|
1576
|
+
await p.screenshot({ path: screenshotPath });
|
|
1577
|
+
return warn(`New challenge appeared: "${instruction}"`, {
|
|
1578
|
+
screenshot: screenshotPath,
|
|
1579
|
+
next: 'Use "captcha-grid" to analyze and "click-tile" to solve',
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
await p.screenshot({ path: screenshotPath });
|
|
1583
|
+
return ok('CAPTCHA verification submitted', {
|
|
1584
|
+
screenshot: screenshotPath,
|
|
1585
|
+
note: 'Check if the form/page progressed. If CAPTCHA reappears, use "captcha-grid" again.',
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
catch (e) {
|
|
1589
|
+
return err(`Verify failed: ${e.message}`, 'Use "captcha-grid" to re-scan and retry');
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
case 'slider-analyze': {
|
|
1593
|
+
const p = await ensureBrowser();
|
|
1594
|
+
const results = [];
|
|
1595
|
+
const sliderInfo = await p.evaluate(() => {
|
|
1596
|
+
const info = {};
|
|
1597
|
+
const track = document.querySelector('.geetest_slider_track, .geetest_slider, [class*="slider_track"], [class*="slider-track"]');
|
|
1598
|
+
if (track) {
|
|
1599
|
+
const trackRect = track.getBoundingClientRect();
|
|
1600
|
+
info.track = { left: trackRect.left, width: trackRect.width, top: trackRect.top };
|
|
1601
|
+
}
|
|
1602
|
+
const cut = document.querySelector('.geetest_cut, .geetest_piece_bg, [class*="geetest_cut"], [class*="slider_cut"], [class*="puzzle-gap"], [class*="slider-gap"]');
|
|
1603
|
+
if (cut) {
|
|
1604
|
+
const cutRect = cut.getBoundingClientRect();
|
|
1605
|
+
const style = window.getComputedStyle(cut);
|
|
1606
|
+
info.cut = {
|
|
1607
|
+
left: cutRect.left,
|
|
1608
|
+
width: cutRect.width,
|
|
1609
|
+
styleLeft: parseFloat(style.left) || null,
|
|
1610
|
+
transform: style.transform || null,
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
const bg = document.querySelector('.geetest_canvas_bg, .geetest_bg, [class*="geetest_canvas"], canvas[class*="bg"]');
|
|
1614
|
+
if (bg) {
|
|
1615
|
+
const bgRect = bg.getBoundingClientRect();
|
|
1616
|
+
info.bg = { left: bgRect.left, width: bgRect.width };
|
|
1617
|
+
}
|
|
1618
|
+
const piece = document.querySelector('.geetest_piece, .geetest_slider_piece, [class*="slider_piece"], [class*="puzzle-piece"]');
|
|
1619
|
+
if (piece) {
|
|
1620
|
+
const pieceRect = piece.getBoundingClientRect();
|
|
1621
|
+
info.piece = { left: pieceRect.left, width: pieceRect.width };
|
|
1622
|
+
}
|
|
1623
|
+
const slider = document.querySelector('.geetest_slider_button, .geetest_slider_knob, [class*="slider_button"], [class*="slider-handle"]');
|
|
1624
|
+
if (slider) {
|
|
1625
|
+
const sliderRect = slider.getBoundingClientRect();
|
|
1626
|
+
info.slider = { left: sliderRect.left, width: sliderRect.width, centerX: sliderRect.left + sliderRect.width / 2 };
|
|
1627
|
+
}
|
|
1628
|
+
info.canvasCount = document.querySelectorAll('canvas').length;
|
|
1629
|
+
info.allGeeTestClasses = Array.from(document.querySelectorAll('[class*="geetest"]')).slice(0, 20).map(el => {
|
|
1630
|
+
const r = el.getBoundingClientRect();
|
|
1631
|
+
return `${el.className?.toString().slice(0, 80)} [${Math.round(r.left)},${Math.round(r.top)} ${Math.round(r.width)}x${Math.round(r.height)}]`;
|
|
1632
|
+
});
|
|
1633
|
+
return info;
|
|
1634
|
+
});
|
|
1635
|
+
const puzzleEl = p.locator('.geetest_panel, .geetest_widget, [class*="geetest_container"], [class*="slider_container"]').first();
|
|
1636
|
+
const screenshotPath = join(homedir(), '.aurix-slider-puzzle.png');
|
|
1637
|
+
try {
|
|
1638
|
+
if (await puzzleEl.count() > 0) {
|
|
1639
|
+
await puzzleEl.screenshot({ path: screenshotPath });
|
|
1640
|
+
}
|
|
1641
|
+
else {
|
|
1642
|
+
await p.screenshot({ path: screenshotPath });
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
catch {
|
|
1646
|
+
await p.screenshot({ path: screenshotPath });
|
|
1647
|
+
}
|
|
1648
|
+
results.push(`Puzzle screenshot: ${screenshotPath}`);
|
|
1649
|
+
results.push('');
|
|
1650
|
+
let gapOffset = null;
|
|
1651
|
+
if (sliderInfo.cut && sliderInfo.bg) {
|
|
1652
|
+
const bgLeft = sliderInfo.bg.left;
|
|
1653
|
+
const cutStyleLeft = sliderInfo.cut.styleLeft;
|
|
1654
|
+
const cutRectLeft = sliderInfo.cut.left;
|
|
1655
|
+
if (cutStyleLeft && cutStyleLeft > 0) {
|
|
1656
|
+
gapOffset = Math.round(cutStyleLeft);
|
|
1657
|
+
results.push(`Gap position (from CSS left): ${gapOffset}px from puzzle left edge`);
|
|
1658
|
+
}
|
|
1659
|
+
else if (cutRectLeft && bgLeft) {
|
|
1660
|
+
gapOffset = Math.round(cutRectLeft - bgLeft);
|
|
1661
|
+
results.push(`Gap position (from rect): ${gapOffset}px from puzzle left edge`);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (gapOffset === null && sliderInfo.cut?.transform && sliderInfo.cut.transform !== 'none') {
|
|
1665
|
+
const match = sliderInfo.cut.transform.match(/matrix\(.*?,\s*([\d.]+)/);
|
|
1666
|
+
if (match) {
|
|
1667
|
+
gapOffset = Math.round(parseFloat(match[1]));
|
|
1668
|
+
results.push(`Gap position (from transform): ${gapOffset}px from puzzle left edge`);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
if (sliderInfo.slider) {
|
|
1672
|
+
results.push(`Slider handle: at x=${Math.round(sliderInfo.slider.left)}, width=${Math.round(sliderInfo.slider.width)}`);
|
|
1673
|
+
}
|
|
1674
|
+
if (sliderInfo.track) {
|
|
1675
|
+
results.push(`Slider track: width=${Math.round(sliderInfo.track.width)}px`);
|
|
1676
|
+
}
|
|
1677
|
+
if (gapOffset !== null && sliderInfo.piece) {
|
|
1678
|
+
const pieceHalfWidth = Math.round((sliderInfo.piece.width || 44) / 2);
|
|
1679
|
+
const adjustedOffset = gapOffset - pieceHalfWidth;
|
|
1680
|
+
results.push('');
|
|
1681
|
+
results.push(`[OK] RECOMMENDED OFFSET: drag-to value="${adjustedOffset},0"`);
|
|
1682
|
+
results.push(`(gap at ${gapOffset}px minus half piece width ${pieceHalfWidth}px = ${adjustedOffset}px)`);
|
|
1683
|
+
}
|
|
1684
|
+
else if (gapOffset !== null) {
|
|
1685
|
+
results.push('');
|
|
1686
|
+
results.push(`[OK] RECOMMENDED OFFSET: drag-to value="${gapOffset},0"`);
|
|
1687
|
+
}
|
|
1688
|
+
else {
|
|
1689
|
+
results.push('');
|
|
1690
|
+
results.push('[WARN] Could not auto-detect gap position from DOM.');
|
|
1691
|
+
results.push('Look at the puzzle screenshot to find where the gap/hole is.');
|
|
1692
|
+
results.push('Estimate the pixel distance from the LEFT edge of the puzzle to the CENTER of the gap.');
|
|
1693
|
+
results.push('Then use: drag-to target=".geetest_slider_button" value="<estimated_px>,0"');
|
|
1694
|
+
}
|
|
1695
|
+
if (sliderInfo.allGeeTestClasses?.length > 0) {
|
|
1696
|
+
results.push('');
|
|
1697
|
+
results.push('GeeTest DOM elements:');
|
|
1698
|
+
sliderInfo.allGeeTestClasses.forEach((c) => results.push(` ${c}`));
|
|
1699
|
+
}
|
|
1700
|
+
return results.join('\n');
|
|
1701
|
+
}
|
|
1702
|
+
case 'signup-assist': {
|
|
1703
|
+
const p = await ensureBrowser();
|
|
1704
|
+
if (!value)
|
|
1705
|
+
return err('signup-assist requires value as JSON', 'Example: value=\'{"email":"user@mail.com","password":"Pass123!","firstName":"John","lastName":"Doe"}\'');
|
|
1706
|
+
let data = {};
|
|
1707
|
+
try {
|
|
1708
|
+
data = JSON.parse(value);
|
|
1709
|
+
}
|
|
1710
|
+
catch {
|
|
1711
|
+
const parts = value.split(',').map(s => s.trim());
|
|
1712
|
+
if (parts.length >= 2) {
|
|
1713
|
+
data.email = parts[0];
|
|
1714
|
+
data.password = parts[1];
|
|
1715
|
+
if (parts[2])
|
|
1716
|
+
data.firstName = parts[2];
|
|
1717
|
+
if (parts[3])
|
|
1718
|
+
data.lastName = parts[3];
|
|
1719
|
+
}
|
|
1720
|
+
else {
|
|
1721
|
+
return err('signup-assist: could not parse value', 'Use JSON: {"email":"...","password":"..."} or comma-separated: "email,password"');
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
const results = [];
|
|
1725
|
+
results.push('=== SIGNUP ASSIST ===');
|
|
1726
|
+
results.push(`Provided: ${Object.keys(data).join(', ')}`);
|
|
1727
|
+
results.push('');
|
|
1728
|
+
const allFrames = [p, ...p.frames().filter(f => f !== p.mainFrame())];
|
|
1729
|
+
let activeFrame = p;
|
|
1730
|
+
for (const frame of allFrames) {
|
|
1731
|
+
const inputs = await frame.locator('input:visible, select:visible, textarea:visible').count();
|
|
1732
|
+
if (inputs > 0) {
|
|
1733
|
+
activeFrame = frame;
|
|
1734
|
+
break;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
results.push(`Active frame: ${activeFrame === p ? 'main page' : 'iframe'} (${await activeFrame.locator('input:visible, select:visible, textarea:visible').count()} fields)`);
|
|
1738
|
+
const fillField = async (selectors, val, label) => {
|
|
1739
|
+
for (const sel of selectors) {
|
|
1740
|
+
try {
|
|
1741
|
+
const loc = activeFrame.locator(sel).first();
|
|
1742
|
+
if (await loc.count() > 0 && await loc.isVisible()) {
|
|
1743
|
+
const cur = await loc.inputValue().catch(() => '');
|
|
1744
|
+
if (cur && cur.length > 0) {
|
|
1745
|
+
results.push(` ✓ ${label}: already filled`);
|
|
1746
|
+
return true;
|
|
1747
|
+
}
|
|
1748
|
+
await loc.fill(val, { timeout: 3000 });
|
|
1749
|
+
results.push(` ✓ ${label}: filled`);
|
|
1750
|
+
return true;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
catch { }
|
|
1754
|
+
}
|
|
1755
|
+
return false;
|
|
1756
|
+
};
|
|
1757
|
+
const clickField = async (selectors, label) => {
|
|
1758
|
+
for (const sel of selectors) {
|
|
1759
|
+
try {
|
|
1760
|
+
const loc = activeFrame.locator(sel).first();
|
|
1761
|
+
if (await loc.count() > 0 && await loc.isVisible()) {
|
|
1762
|
+
const checked = await loc.isChecked().catch(() => false);
|
|
1763
|
+
if (checked) {
|
|
1764
|
+
results.push(` ✓ ${label}: already checked`);
|
|
1765
|
+
return true;
|
|
1766
|
+
}
|
|
1767
|
+
await loc.click({ timeout: 3000 });
|
|
1768
|
+
results.push(` ✓ ${label}: clicked`);
|
|
1769
|
+
return true;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
catch { }
|
|
1773
|
+
}
|
|
1774
|
+
return false;
|
|
1775
|
+
};
|
|
1776
|
+
const selectDropdown = async (selectors, val, label) => {
|
|
1777
|
+
for (const sel of selectors) {
|
|
1778
|
+
try {
|
|
1779
|
+
const loc = activeFrame.locator(sel).first();
|
|
1780
|
+
if (await loc.count() > 0 && await loc.isVisible()) {
|
|
1781
|
+
const tag = await loc.evaluate((el) => el.tagName.toLowerCase()).catch(() => '');
|
|
1782
|
+
if (tag === 'select') {
|
|
1783
|
+
try {
|
|
1784
|
+
await loc.selectOption({ label: val }, { timeout: 3000 });
|
|
1785
|
+
results.push(` ✓ ${label}: selected "${val}"`);
|
|
1786
|
+
return true;
|
|
1787
|
+
}
|
|
1788
|
+
catch {
|
|
1789
|
+
try {
|
|
1790
|
+
await loc.selectOption({ value: val }, { timeout: 3000 });
|
|
1791
|
+
results.push(` ✓ ${label}: selected value "${val}"`);
|
|
1792
|
+
return true;
|
|
1793
|
+
}
|
|
1794
|
+
catch { }
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
await loc.click({ timeout: 3000 });
|
|
1798
|
+
await activeFrame.waitForTimeout(400);
|
|
1799
|
+
const numVal = parseInt(val, 10);
|
|
1800
|
+
const optSels = [
|
|
1801
|
+
`[role="option"]:has-text("${val}")`,
|
|
1802
|
+
`[role="listbox"] [role="option"]:has-text("${val}")`,
|
|
1803
|
+
`li:has-text("${val}"):visible`,
|
|
1804
|
+
`[data-value="${val}"]:visible`,
|
|
1805
|
+
];
|
|
1806
|
+
if (!isNaN(numVal)) {
|
|
1807
|
+
optSels.push(`[role="option"]:nth-child(${numVal})`);
|
|
1808
|
+
optSels.push(`li:nth-child(${numVal}):visible`);
|
|
1809
|
+
}
|
|
1810
|
+
for (const os of optSels) {
|
|
1811
|
+
try {
|
|
1812
|
+
const opt = activeFrame.locator(os).first();
|
|
1813
|
+
if (await opt.count() > 0 && await opt.isVisible()) {
|
|
1814
|
+
await opt.click({ timeout: 2000 });
|
|
1815
|
+
results.push(` ✓ ${label}: selected "${val}" (dropdown)`);
|
|
1816
|
+
return true;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
catch { }
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
catch { }
|
|
1824
|
+
}
|
|
1825
|
+
return false;
|
|
1826
|
+
};
|
|
1827
|
+
results.push('');
|
|
1828
|
+
results.push('--- Filling fields ---');
|
|
1829
|
+
if (data.email) {
|
|
1830
|
+
await fillField([
|
|
1831
|
+
'input[type="email"]',
|
|
1832
|
+
'input[name*="email" i]', 'input[name*="Email"]',
|
|
1833
|
+
'input[name*="username" i]', 'input[name*="MemberName"]',
|
|
1834
|
+
'input[id*="email" i]', 'input[id*="username" i]',
|
|
1835
|
+
'input[placeholder*="email" i]', 'input[placeholder*="Email"]',
|
|
1836
|
+
'input[autocomplete="email"]', 'input[autocomplete="username"]',
|
|
1837
|
+
'input[name="loginfmt"]',
|
|
1838
|
+
], data.email, 'Email');
|
|
1839
|
+
}
|
|
1840
|
+
if (data.password) {
|
|
1841
|
+
await fillField([
|
|
1842
|
+
'input[type="password"]',
|
|
1843
|
+
'input[name*="password" i]', 'input[name*="Password"]', 'input[name*="Passwd"]',
|
|
1844
|
+
'input[id*="password" i]', 'input[name*="pass" i]',
|
|
1845
|
+
'input[autocomplete="new-password"]', 'input[autocomplete="current-password"]',
|
|
1846
|
+
], data.password, 'Password');
|
|
1847
|
+
}
|
|
1848
|
+
if (data.firstName) {
|
|
1849
|
+
await fillField([
|
|
1850
|
+
'input[name*="firstName" i]', 'input[name*="FirstName"]', 'input[name*="fname" i]',
|
|
1851
|
+
'input[id*="firstName" i]', 'input[id*="fname" i]',
|
|
1852
|
+
'input[autocomplete="given-name"]',
|
|
1853
|
+
'input[placeholder*="first name" i]', 'input[placeholder*="First"]',
|
|
1854
|
+
'input[name="NameInput"]',
|
|
1855
|
+
], data.firstName, 'First name');
|
|
1856
|
+
}
|
|
1857
|
+
if (data.lastName) {
|
|
1858
|
+
await fillField([
|
|
1859
|
+
'input[name*="lastName" i]', 'input[name*="LastName"]', 'input[name*="lname" i]',
|
|
1860
|
+
'input[id*="lastName" i]', 'input[id*="lname" i]',
|
|
1861
|
+
'input[autocomplete="family-name"]',
|
|
1862
|
+
'input[placeholder*="last name" i]', 'input[placeholder*="Last"]',
|
|
1863
|
+
'input[name="LastName"]',
|
|
1864
|
+
], data.lastName, 'Last name');
|
|
1865
|
+
}
|
|
1866
|
+
if (data.firstName && !data.lastName) {
|
|
1867
|
+
await fillField([
|
|
1868
|
+
'input[name*="name" i]', 'input[id*="name" i]',
|
|
1869
|
+
'input[autocomplete="name"]',
|
|
1870
|
+
], data.firstName + ' User', 'Full name');
|
|
1871
|
+
}
|
|
1872
|
+
if (data.phone) {
|
|
1873
|
+
await fillField([
|
|
1874
|
+
'input[type="tel"]', 'input[name*="phone" i]', 'input[name*="Phone"]',
|
|
1875
|
+
'input[id*="phone" i]', 'input[autocomplete="tel"]',
|
|
1876
|
+
'input[placeholder*="phone" i]',
|
|
1877
|
+
], data.phone, 'Phone');
|
|
1878
|
+
}
|
|
1879
|
+
const birthYear = data.birthYear || '2003';
|
|
1880
|
+
const birthMonth = data.birthMonth || 'January';
|
|
1881
|
+
const birthDay = data.birthDay || '15';
|
|
1882
|
+
await selectDropdown([
|
|
1883
|
+
'select[id*="BirthYear"]', 'select[name*="birthYear" i]', 'select[id*="year" i]',
|
|
1884
|
+
'select[name*="year" i]', 'select[aria-label*="year" i]', 'select[aria-label*="Birth"]',
|
|
1885
|
+
], birthYear, 'Birth year');
|
|
1886
|
+
await selectDropdown([
|
|
1887
|
+
'select[id*="BirthMonth"]', 'select[name*="birthMonth" i]', 'select[id*="month" i]',
|
|
1888
|
+
'select[name*="month" i]', 'select[aria-label*="month" i]',
|
|
1889
|
+
], birthMonth, 'Birth month');
|
|
1890
|
+
await selectDropdown([
|
|
1891
|
+
'select[id*="BirthDay"]', 'select[name*="birthDay" i]', 'select[id*="day" i]',
|
|
1892
|
+
'select[name*="day" i]', 'select[aria-label*="day" i]',
|
|
1893
|
+
], birthDay, 'Birth day');
|
|
1894
|
+
await selectDropdown([
|
|
1895
|
+
'select[id*="Country"]', 'select[name*="country" i]',
|
|
1896
|
+
'select[aria-label*="country" i]', 'select[name*="Country"]',
|
|
1897
|
+
], data.country || 'United States', 'Country');
|
|
1898
|
+
await fillField([
|
|
1899
|
+
'input[name*="username" i]', 'input[id*="username" i]',
|
|
1900
|
+
'input[placeholder*="username" i]', 'input[name*="Username"]',
|
|
1901
|
+
], data.username || (data.email ? data.email.split('@')[0] + Math.floor(Math.random() * 999) : 'user' + Math.floor(Math.random() * 9999)), 'Username');
|
|
1902
|
+
await clickField([
|
|
1903
|
+
'input[type="checkbox"][name*="agree" i]',
|
|
1904
|
+
'input[type="checkbox"][name*="tos" i]',
|
|
1905
|
+
'input[type="checkbox"][name*="terms" i]',
|
|
1906
|
+
'input[type="checkbox"][name*="consent" i]',
|
|
1907
|
+
'input[type="checkbox"][name*="privacy" i]',
|
|
1908
|
+
'input[type="checkbox"][name*="policy" i]',
|
|
1909
|
+
'input[type="checkbox"][id*="agree" i]',
|
|
1910
|
+
'input[type="checkbox"][id*="terms" i]',
|
|
1911
|
+
'input[type="checkbox"][id*="consent" i]',
|
|
1912
|
+
'input[type="checkbox"][id*="privacy" i]',
|
|
1913
|
+
'input[type="checkbox"][aria-label*="agree" i]',
|
|
1914
|
+
'input[type="checkbox"][aria-label*="terms" i]',
|
|
1915
|
+
'input[type="checkbox"][aria-label*="consent" i]',
|
|
1916
|
+
'input[type="checkbox"][aria-label*="accept" i]',
|
|
1917
|
+
'label:has-text("agree") input[type="checkbox"]',
|
|
1918
|
+
'label:has-text("terms") input[type="checkbox"]',
|
|
1919
|
+
'label:has-text("accept") input[type="checkbox"]',
|
|
1920
|
+
'label:has-text("consent") input[type="checkbox"]',
|
|
1921
|
+
'label:has-text("privacy") input[type="checkbox"]',
|
|
1922
|
+
'label:has-text("I agree") input[type="checkbox"]',
|
|
1923
|
+
'label:has-text("I accept") input[type="checkbox"]',
|
|
1924
|
+
'[role="checkbox"][aria-checked="false"]',
|
|
1925
|
+
'div:has-text("I agree"):not(:has(div:has-text("I agree")))',
|
|
1926
|
+
'span:has-text("I agree"):not(:has(span:has-text("I agree")))',
|
|
1927
|
+
], 'Terms/Agreement checkbox');
|
|
1928
|
+
await p.waitForTimeout(500);
|
|
1929
|
+
const hasCaptcha = p.frames().some(f => {
|
|
1930
|
+
const u = f.url();
|
|
1931
|
+
return u.includes('recaptcha') || u.includes('hcaptcha') || u.includes('funcaptcha') ||
|
|
1932
|
+
u.includes('arkoselabs') || u.includes('geetest') || u.includes('turnstile') ||
|
|
1933
|
+
u.includes('captcha') || u.includes('mtcaptcha');
|
|
1934
|
+
});
|
|
1935
|
+
if (hasCaptcha) {
|
|
1936
|
+
results.push('');
|
|
1937
|
+
results.push('--- Verification step detected ---');
|
|
1938
|
+
results.push('Attempting to complete automatically...');
|
|
1939
|
+
const solveResults = await autoSolveCaptcha(p);
|
|
1940
|
+
solveResults.forEach(r => results.push(` ${r}`));
|
|
1941
|
+
const needsVision = solveResults.some(r => r.includes('CAPTCHA SOLVING STEPS') || r.includes('REQUIRES_VISION'));
|
|
1942
|
+
if (!needsVision) {
|
|
1943
|
+
await p.waitForTimeout(2000);
|
|
1944
|
+
results.push('Verification completed. Continuing form submission...');
|
|
1945
|
+
}
|
|
1946
|
+
else {
|
|
1947
|
+
results.push('');
|
|
1948
|
+
results.push('⚠ CAPTCHA grid analysis is above. Follow the CAPTCHA SOLVING STEPS to complete it, then re-run signup-assist to continue.');
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
const clicked = await clickField([
|
|
1952
|
+
'button[type="submit"]',
|
|
1953
|
+
'input[type="submit"]',
|
|
1954
|
+
'button:has-text("Next")', 'button:has-text("next")',
|
|
1955
|
+
'button:has-text("Continue")', 'button:has-text("continue")',
|
|
1956
|
+
'button:has-text("Submit")', 'button:has-text("submit")',
|
|
1957
|
+
'button:has-text("Create")', 'button:has-text("create")',
|
|
1958
|
+
'button:has-text("Sign up")', 'button:has-text("sign up")',
|
|
1959
|
+
'button:has-text("Register")', 'button:has-text("register")',
|
|
1960
|
+
'button:has-text("Accept")', 'button:has-text("accept")',
|
|
1961
|
+
'#iSignupAction', '#signup-button', '#submit-btn',
|
|
1962
|
+
'button.fui-Button[type="button"]:visible',
|
|
1963
|
+
], 'Submit/Next button');
|
|
1964
|
+
await p.waitForTimeout(2000);
|
|
1965
|
+
results.push('');
|
|
1966
|
+
results.push(`--- Result ---`);
|
|
1967
|
+
results.push(`URL: ${p.url()}`);
|
|
1968
|
+
results.push(`Title: ${await p.title()}`);
|
|
1969
|
+
const screenshotPath = join(homedir(), '.aurix-signup-result.png');
|
|
1970
|
+
await p.screenshot({ path: screenshotPath });
|
|
1971
|
+
results.push(`Screenshot: ${screenshotPath}`);
|
|
1972
|
+
const newInputs = await (async () => {
|
|
1973
|
+
for (const frame of [p, ...p.frames().filter(f => f !== p.mainFrame())]) {
|
|
1974
|
+
const count = await frame.locator('input:visible').count();
|
|
1975
|
+
if (count > 0)
|
|
1976
|
+
return count;
|
|
1977
|
+
}
|
|
1978
|
+
return 0;
|
|
1979
|
+
})();
|
|
1980
|
+
if (newInputs > 0) {
|
|
1981
|
+
results.push('');
|
|
1982
|
+
results.push(`${newInputs} input field(s) still visible — run signup-assist again with any remaining data`);
|
|
1983
|
+
results.push('Common next fields: password, first name, last name, birth date, phone');
|
|
1984
|
+
}
|
|
1985
|
+
return results.join('\n');
|
|
1986
|
+
}
|
|
1987
|
+
case 'signin-assist': {
|
|
1988
|
+
const p = await ensureBrowser();
|
|
1989
|
+
if (!value)
|
|
1990
|
+
return err('signin-assist requires value as JSON', 'Example: value=\'{"email":"user@mail.com","password":"Pass123!"}\'');
|
|
1991
|
+
let data = {};
|
|
1992
|
+
try {
|
|
1993
|
+
data = JSON.parse(value);
|
|
1994
|
+
}
|
|
1995
|
+
catch {
|
|
1996
|
+
const parts = value.split(',').map(s => s.trim());
|
|
1997
|
+
if (parts.length >= 2) {
|
|
1998
|
+
data.email = parts[0];
|
|
1999
|
+
data.password = parts[1];
|
|
2000
|
+
}
|
|
2001
|
+
else {
|
|
2002
|
+
return err('signin-assist: could not parse value', 'Use JSON: {"email":"...","password":"..."} or comma-separated: "email,password"');
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
const results = [];
|
|
2006
|
+
results.push('=== SIGNIN ASSIST ===');
|
|
2007
|
+
const allFrames = [p, ...p.frames().filter(f => f !== p.mainFrame())];
|
|
2008
|
+
let activeFrame = p;
|
|
2009
|
+
for (const frame of allFrames) {
|
|
2010
|
+
const inputs = await frame.locator('input:visible').count();
|
|
2011
|
+
if (inputs > 0) {
|
|
2012
|
+
activeFrame = frame;
|
|
2013
|
+
break;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
results.push(`Active frame: ${activeFrame === p ? 'main page' : 'iframe'}`);
|
|
2017
|
+
const fillField = async (selectors, val, label) => {
|
|
2018
|
+
for (const sel of selectors) {
|
|
2019
|
+
try {
|
|
2020
|
+
const loc = activeFrame.locator(sel).first();
|
|
2021
|
+
if (await loc.count() > 0 && await loc.isVisible()) {
|
|
2022
|
+
const cur = await loc.inputValue().catch(() => '');
|
|
2023
|
+
if (cur && cur.length > 0) {
|
|
2024
|
+
results.push(` ✓ ${label}: already filled`);
|
|
2025
|
+
return true;
|
|
2026
|
+
}
|
|
2027
|
+
await loc.fill(val, { timeout: 3000 });
|
|
2028
|
+
results.push(` ✓ ${label}: filled`);
|
|
2029
|
+
return true;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
catch { }
|
|
2033
|
+
}
|
|
2034
|
+
return false;
|
|
2035
|
+
};
|
|
2036
|
+
const clickField = async (selectors, label) => {
|
|
2037
|
+
for (const sel of selectors) {
|
|
2038
|
+
try {
|
|
2039
|
+
const loc = activeFrame.locator(sel).first();
|
|
2040
|
+
if (await loc.count() > 0 && await loc.isVisible()) {
|
|
2041
|
+
const checked = await loc.isChecked().catch(() => false);
|
|
2042
|
+
if (checked) {
|
|
2043
|
+
results.push(` ✓ ${label}: already checked`);
|
|
2044
|
+
return true;
|
|
2045
|
+
}
|
|
2046
|
+
await loc.click({ timeout: 3000 });
|
|
2047
|
+
results.push(` ✓ ${label}: clicked`);
|
|
2048
|
+
return true;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
catch { }
|
|
2052
|
+
}
|
|
2053
|
+
return false;
|
|
2054
|
+
};
|
|
2055
|
+
results.push('');
|
|
2056
|
+
results.push('--- Filling login ---');
|
|
2057
|
+
if (data.email) {
|
|
2058
|
+
await fillField([
|
|
2059
|
+
'input[type="email"]',
|
|
2060
|
+
'input[name*="email" i]', 'input[name*="Email"]',
|
|
2061
|
+
'input[name*="username" i]', 'input[name*="MemberName"]',
|
|
2062
|
+
'input[id*="email" i]', 'input[id*="username" i]',
|
|
2063
|
+
'input[placeholder*="email" i]', 'input[placeholder*="Email"]',
|
|
2064
|
+
'input[autocomplete="email"]', 'input[autocomplete="username"]',
|
|
2065
|
+
'input[name="loginfmt"]', 'input[name="login"]',
|
|
2066
|
+
'input[type="text"][name*="user" i]',
|
|
2067
|
+
], data.email, 'Email/Username');
|
|
2068
|
+
}
|
|
2069
|
+
if (data.password) {
|
|
2070
|
+
await fillField([
|
|
2071
|
+
'input[type="password"]',
|
|
2072
|
+
'input[name*="password" i]', 'input[name*="Password"]', 'input[name*="Passwd"]',
|
|
2073
|
+
'input[id*="password" i]', 'input[name*="pass" i]',
|
|
2074
|
+
'input[autocomplete="current-password"]',
|
|
2075
|
+
], data.password, 'Password');
|
|
2076
|
+
}
|
|
2077
|
+
await clickField([
|
|
2078
|
+
'input[type="checkbox"][name*="remember" i]',
|
|
2079
|
+
'input[type="checkbox"][id*="remember" i]',
|
|
2080
|
+
'label:has-text("remember") input[type="checkbox"]',
|
|
2081
|
+
'label:has-text("Keep me") input[type="checkbox"]',
|
|
2082
|
+
'label:has-text("Stay signed") input[type="checkbox"]',
|
|
2083
|
+
], 'Remember me checkbox');
|
|
2084
|
+
await p.waitForTimeout(300);
|
|
2085
|
+
const hasCaptcha = p.frames().some(f => {
|
|
2086
|
+
const u = f.url();
|
|
2087
|
+
return u.includes('recaptcha') || u.includes('hcaptcha') || u.includes('funcaptcha') ||
|
|
2088
|
+
u.includes('arkoselabs') || u.includes('geetest') || u.includes('turnstile') ||
|
|
2089
|
+
u.includes('captcha') || u.includes('mtcaptcha');
|
|
2090
|
+
});
|
|
2091
|
+
if (hasCaptcha) {
|
|
2092
|
+
results.push('');
|
|
2093
|
+
results.push('--- Verification step detected ---');
|
|
2094
|
+
results.push('Attempting to complete automatically...');
|
|
2095
|
+
const solveResults = await autoSolveCaptcha(p);
|
|
2096
|
+
solveResults.forEach(r => results.push(` ${r}`));
|
|
2097
|
+
const needsVision = solveResults.some(r => r.includes('CAPTCHA SOLVING STEPS') || r.includes('REQUIRES_VISION'));
|
|
2098
|
+
if (!needsVision) {
|
|
2099
|
+
await p.waitForTimeout(2000);
|
|
2100
|
+
results.push('Verification completed. Continuing login...');
|
|
2101
|
+
}
|
|
2102
|
+
else {
|
|
2103
|
+
results.push('');
|
|
2104
|
+
results.push('⚠ CAPTCHA grid analysis is above. Follow the CAPTCHA SOLVING STEPS to complete it, then re-run signin-assist to continue.');
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
await clickField([
|
|
2108
|
+
'button[type="submit"]',
|
|
2109
|
+
'input[type="submit"]',
|
|
2110
|
+
'button:has-text("Sign in")', 'button:has-text("sign in")',
|
|
2111
|
+
'button:has-text("Log in")', 'button:has-text("log in")',
|
|
2112
|
+
'button:has-text("Login")', 'button:has-text("login")',
|
|
2113
|
+
'button:has-text("Next")', 'button:has-text("next")',
|
|
2114
|
+
'button:has-text("Continue")', 'button:has-text("continue")',
|
|
2115
|
+
'button:has-text("Submit")',
|
|
2116
|
+
'#idSIButton9', '#loginButton', '#submitBtn',
|
|
2117
|
+
'button.fui-Button[type="button"]:visible',
|
|
2118
|
+
'a:has-text("Sign in")', 'a:has-text("Log in")',
|
|
2119
|
+
], 'Login/Sign in button');
|
|
2120
|
+
await p.waitForTimeout(2000);
|
|
2121
|
+
results.push('');
|
|
2122
|
+
results.push(`--- Result ---`);
|
|
2123
|
+
results.push(`URL: ${p.url()}`);
|
|
2124
|
+
results.push(`Title: ${await p.title()}`);
|
|
2125
|
+
const screenshotPath = join(homedir(), '.aurix-signin-result.png');
|
|
2126
|
+
await p.screenshot({ path: screenshotPath });
|
|
2127
|
+
results.push(`Screenshot: ${screenshotPath}`);
|
|
2128
|
+
const has2FA = await (async () => {
|
|
2129
|
+
for (const frame of allFrames) {
|
|
2130
|
+
const otp = await frame.locator('input[name*="otp" i], input[name*="code" i], input[name*="verification" i], input[placeholder*="code" i], input[type="tel"]:visible').count();
|
|
2131
|
+
if (otp > 0)
|
|
2132
|
+
return true;
|
|
2133
|
+
}
|
|
2134
|
+
return false;
|
|
2135
|
+
})();
|
|
2136
|
+
if (has2FA) {
|
|
2137
|
+
results.push('');
|
|
2138
|
+
results.push('2FA/OTP field detected — enter the verification code when available');
|
|
2139
|
+
results.push('Use: fill target="input[name*=\\"code\\"]" value="<code>"');
|
|
2140
|
+
}
|
|
2141
|
+
return results.join('\n');
|
|
2142
|
+
}
|
|
2143
|
+
case 'drag-to': {
|
|
2144
|
+
const p = await ensureBrowser();
|
|
2145
|
+
if (!target)
|
|
2146
|
+
return err('drag-to requires a target (source element or CSS selector)', 'Example: target=".slider-handle" value="200,0" or target="#puzzle-piece" value=".drop-zone"');
|
|
2147
|
+
if (!value)
|
|
2148
|
+
return err('drag-to requires a value (offset "x,y" or target element selector)', 'Offset: "200,0" for 200px right. Element: ".drop-zone" to drag to another element.');
|
|
2149
|
+
try {
|
|
2150
|
+
const sourceEl = p.locator(target).first();
|
|
2151
|
+
const sourceBox = await sourceEl.boundingBox();
|
|
2152
|
+
if (!sourceBox)
|
|
2153
|
+
return err(`Source element "${target}" not found or not visible`);
|
|
2154
|
+
const startX = sourceBox.x + sourceBox.width / 2;
|
|
2155
|
+
const startY = sourceBox.y + sourceBox.height / 2;
|
|
2156
|
+
let endX, endY;
|
|
2157
|
+
const coords = value.split(',').map(s => parseInt(s.trim()));
|
|
2158
|
+
if (coords.length === 2 && !isNaN(coords[0]) && !isNaN(coords[1])) {
|
|
2159
|
+
endX = startX + coords[0];
|
|
2160
|
+
endY = startY + coords[1];
|
|
2161
|
+
}
|
|
2162
|
+
else {
|
|
2163
|
+
const targetEl = p.locator(value).first();
|
|
2164
|
+
const targetBox = await targetEl.boundingBox();
|
|
2165
|
+
if (!targetBox)
|
|
2166
|
+
return err(`Target element "${value}" not found or not visible`);
|
|
2167
|
+
endX = targetBox.x + targetBox.width / 2;
|
|
2168
|
+
endY = targetBox.y + targetBox.height / 2;
|
|
2169
|
+
}
|
|
2170
|
+
await p.mouse.move(startX, startY);
|
|
2171
|
+
await p.waitForTimeout(100 + Math.random() * 150);
|
|
2172
|
+
await p.mouse.down();
|
|
2173
|
+
await p.waitForTimeout(200 + Math.random() * 200);
|
|
2174
|
+
const steps = 15 + Math.floor(Math.random() * 10);
|
|
2175
|
+
for (let i = 1; i <= steps; i++) {
|
|
2176
|
+
const progress = i / steps;
|
|
2177
|
+
const eased = progress < 0.5
|
|
2178
|
+
? 2 * progress * progress
|
|
2179
|
+
: 1 - Math.pow(-2 * progress + 2, 2) / 2;
|
|
2180
|
+
const x = startX + (endX - startX) * eased + (Math.random() - 0.5) * 2;
|
|
2181
|
+
const y = startY + (endY - startY) * eased + (Math.random() - 0.5) * 2;
|
|
2182
|
+
await p.mouse.move(x, y);
|
|
2183
|
+
await p.waitForTimeout(10 + Math.random() * 25);
|
|
2184
|
+
}
|
|
2185
|
+
await p.mouse.move(endX, endY);
|
|
2186
|
+
await p.waitForTimeout(100 + Math.random() * 200);
|
|
2187
|
+
await p.mouse.up();
|
|
2188
|
+
await p.waitForTimeout(500);
|
|
2189
|
+
const screenshotPath = join(homedir(), '.aurix-drag-result.png');
|
|
2190
|
+
await p.screenshot({ path: screenshotPath });
|
|
2191
|
+
return ok(`Dragged "${target}" to (${Math.round(endX)}, ${Math.round(endY)})`, {
|
|
2192
|
+
from: `(${Math.round(startX)}, ${Math.round(startY)})`,
|
|
2193
|
+
to: `(${Math.round(endX)}, ${Math.round(endY)})`,
|
|
2194
|
+
offset: `${Math.round(endX - startX)}, ${Math.round(endY - startY)}`,
|
|
2195
|
+
screenshot: screenshotPath,
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
catch (e) {
|
|
2199
|
+
return err(`Drag failed: ${e.message}`, 'Check if the element exists with "snapshot"');
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
case 'hold-click': {
|
|
2203
|
+
const p = await ensureBrowser();
|
|
2204
|
+
if (!target)
|
|
2205
|
+
return err('hold-click requires a target element');
|
|
2206
|
+
const duration = parseInt(value) || 3000;
|
|
2207
|
+
try {
|
|
2208
|
+
const el = p.locator(target).first();
|
|
2209
|
+
const box = await el.boundingBox();
|
|
2210
|
+
if (!box)
|
|
2211
|
+
return err(`Element "${target}" not found or not visible`);
|
|
2212
|
+
const x = box.x + box.width / 2;
|
|
2213
|
+
const y = box.y + box.height / 2;
|
|
2214
|
+
await p.mouse.move(x, y);
|
|
2215
|
+
await p.waitForTimeout(100 + Math.random() * 100);
|
|
2216
|
+
await p.mouse.down();
|
|
2217
|
+
const holdSteps = Math.floor(duration / 100);
|
|
2218
|
+
for (let i = 0; i < holdSteps; i++) {
|
|
2219
|
+
const jitterX = x + (Math.random() - 0.5) * 3;
|
|
2220
|
+
const jitterY = y + (Math.random() - 0.5) * 3;
|
|
2221
|
+
await p.mouse.move(jitterX, jitterY);
|
|
2222
|
+
await p.waitForTimeout(80 + Math.random() * 40);
|
|
2223
|
+
}
|
|
2224
|
+
await p.mouse.up();
|
|
2225
|
+
await p.waitForTimeout(500);
|
|
2226
|
+
const screenshotPath = join(homedir(), '.aurix-hold-result.png');
|
|
2227
|
+
await p.screenshot({ path: screenshotPath });
|
|
2228
|
+
return ok(`Held click on "${target}" for ${duration}ms`, {
|
|
2229
|
+
position: `(${Math.round(x)}, ${Math.round(y)})`,
|
|
2230
|
+
screenshot: screenshotPath,
|
|
2231
|
+
});
|
|
2232
|
+
}
|
|
2233
|
+
catch (e) {
|
|
2234
|
+
return err(`Hold-click failed: ${e.message}`, 'Check if the element exists with "snapshot"');
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
default:
|
|
2238
|
+
return `Unknown action: "${action}". Available: navigate, click, fill, type, screenshot, snapshot, text, html, url, title, scroll, back, forward, press-key, select, wait, evaluate, new-tab, switch-tab, close-tab, open-tabs, cookies, upload, signup-assist, signin-assist, set-proxy, set-ui, detect-captcha, solve-captcha, captcha-grid, click-tile, captcha-verify, slider-analyze, drag-to, hold-click, close, status`;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
catch (e) {
|
|
2242
|
+
const msg = e.message || String(e);
|
|
2243
|
+
if (msg.includes('Timeout')) {
|
|
2244
|
+
return `Browser timeout: ${msg}\n\nTry: increase timeout in options, use "wait" action first, or check if the element exists with "snapshot".`;
|
|
2245
|
+
}
|
|
2246
|
+
if (msg.includes('strict mode') || msg.includes('more than one')) {
|
|
2247
|
+
return `Multiple elements matched "${target}". Use a more specific selector (CSS, role=, placeholder=) or add .first()/.nth(0).`;
|
|
2248
|
+
}
|
|
2249
|
+
return `Browser error: ${msg}`;
|
|
2250
|
+
}
|
|
2251
|
+
},
|
|
2252
|
+
};
|
|
2253
|
+
//# sourceMappingURL=Browser.js.map
|