wyxrouter 0.4.71
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/CHANGELOG.md +222 -0
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/README.zh-CN.md +1311 -0
- package/assets/pixel-router-2d.png +0 -0
- package/cli/LICENSE +42 -0
- package/cli/README.md +125 -0
- package/cli/app/package.json +58 -0
- package/cli/cli.js +806 -0
- package/cli/package.json +48 -0
- package/i18n/README.ja-JP.md +1209 -0
- package/i18n/README.ru.md +1311 -0
- package/i18n/README.vi.md +1310 -0
- package/i18n/README.zh-CN.md +1306 -0
- package/images/9router.png +0 -0
- package/jsconfig.json +12 -0
- package/next.config.mjs +71 -0
- package/open-sse/config/appConstants.js +203 -0
- package/open-sse/config/codexInstructions.js +119 -0
- package/open-sse/config/constants.js +4 -0
- package/open-sse/config/defaultThinkingSignature.js +12 -0
- package/open-sse/config/errorConfig.js +85 -0
- package/open-sse/config/googleTtsLanguages.js +62 -0
- package/open-sse/config/kiroConstants.js +322 -0
- package/open-sse/config/models.js +13 -0
- package/open-sse/config/ollamaModels.js +19 -0
- package/open-sse/config/providerModels.js +944 -0
- package/open-sse/config/providers.js +458 -0
- package/open-sse/config/runtimeConfig.js +72 -0
- package/open-sse/config/ttsModels.js +129 -0
- package/open-sse/executors/antigravity.js +504 -0
- package/open-sse/executors/azure.js +57 -0
- package/open-sse/executors/base.js +185 -0
- package/open-sse/executors/codex.js +469 -0
- package/open-sse/executors/commandcode.js +88 -0
- package/open-sse/executors/cursor.js +795 -0
- package/open-sse/executors/default.js +497 -0
- package/open-sse/executors/gemini-cli.js +89 -0
- package/open-sse/executors/github.js +379 -0
- package/open-sse/executors/grok-web.js +345 -0
- package/open-sse/executors/iflow.js +108 -0
- package/open-sse/executors/index.js +75 -0
- package/open-sse/executors/kiro.js +508 -0
- package/open-sse/executors/ollama-local.js +14 -0
- package/open-sse/executors/opencode-go.js +41 -0
- package/open-sse/executors/opencode.js +32 -0
- package/open-sse/executors/perplexity-web.js +507 -0
- package/open-sse/executors/qoder.js +450 -0
- package/open-sse/executors/qwen.js +129 -0
- package/open-sse/executors/vertex.js +131 -0
- package/open-sse/executors/xiaomi-tokenplan.js +19 -0
- package/open-sse/handlers/chatCore/nonStreamingHandler.js +230 -0
- package/open-sse/handlers/chatCore/requestDetail.js +102 -0
- package/open-sse/handlers/chatCore/sseToJsonHandler.js +231 -0
- package/open-sse/handlers/chatCore/streamingHandler.js +103 -0
- package/open-sse/handlers/chatCore.js +287 -0
- package/open-sse/handlers/embeddingProviders/_base.js +4 -0
- package/open-sse/handlers/embeddingProviders/gemini.js +54 -0
- package/open-sse/handlers/embeddingProviders/index.js +23 -0
- package/open-sse/handlers/embeddingProviders/openai.js +39 -0
- package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +13 -0
- package/open-sse/handlers/embeddingsCore.js +126 -0
- package/open-sse/handlers/fetch/index.js +237 -0
- package/open-sse/handlers/imageGenerationCore.js +189 -0
- package/open-sse/handlers/imageProviders/_base.js +31 -0
- package/open-sse/handlers/imageProviders/blackForestLabs.js +43 -0
- package/open-sse/handlers/imageProviders/cloudflareAi.js +178 -0
- package/open-sse/handlers/imageProviders/codex.js +198 -0
- package/open-sse/handlers/imageProviders/comfyui.js +8 -0
- package/open-sse/handlers/imageProviders/falAi.js +41 -0
- package/open-sse/handlers/imageProviders/gemini.js +25 -0
- package/open-sse/handlers/imageProviders/huggingface.js +22 -0
- package/open-sse/handlers/imageProviders/index.js +40 -0
- package/open-sse/handlers/imageProviders/nanobanana.js +58 -0
- package/open-sse/handlers/imageProviders/openai.js +40 -0
- package/open-sse/handlers/imageProviders/runwayml.js +47 -0
- package/open-sse/handlers/imageProviders/sdwebui.js +17 -0
- package/open-sse/handlers/imageProviders/stabilityAi.js +34 -0
- package/open-sse/handlers/responsesHandler.js +103 -0
- package/open-sse/handlers/search/callers.js +371 -0
- package/open-sse/handlers/search/chatSearch.js +409 -0
- package/open-sse/handlers/search/index.js +201 -0
- package/open-sse/handlers/search/normalizers.js +223 -0
- package/open-sse/handlers/sttCore.js +194 -0
- package/open-sse/handlers/ttsCore.js +74 -0
- package/open-sse/handlers/ttsProviders/_base.js +39 -0
- package/open-sse/handlers/ttsProviders/edgeTts.js +89 -0
- package/open-sse/handlers/ttsProviders/elevenlabs.js +48 -0
- package/open-sse/handlers/ttsProviders/gemini.js +117 -0
- package/open-sse/handlers/ttsProviders/genericFormats.js +169 -0
- package/open-sse/handlers/ttsProviders/googleTts.js +54 -0
- package/open-sse/handlers/ttsProviders/index.js +50 -0
- package/open-sse/handlers/ttsProviders/localDevice.js +87 -0
- package/open-sse/handlers/ttsProviders/minimax.js +59 -0
- package/open-sse/handlers/ttsProviders/openai.js +30 -0
- package/open-sse/handlers/ttsProviders/openrouter.js +70 -0
- package/open-sse/index.js +82 -0
- package/open-sse/rtk/applyFilter.js +15 -0
- package/open-sse/rtk/autodetect.js +111 -0
- package/open-sse/rtk/caveman.js +100 -0
- package/open-sse/rtk/cavemanPrompts.js +78 -0
- package/open-sse/rtk/constants.js +55 -0
- package/open-sse/rtk/filters/buildOutput.js +127 -0
- package/open-sse/rtk/filters/dedupLog.js +44 -0
- package/open-sse/rtk/filters/find.js +49 -0
- package/open-sse/rtk/filters/gitDiff.js +92 -0
- package/open-sse/rtk/filters/gitStatus.js +117 -0
- package/open-sse/rtk/filters/grep.js +48 -0
- package/open-sse/rtk/filters/ls.js +79 -0
- package/open-sse/rtk/filters/readNumbered.js +27 -0
- package/open-sse/rtk/filters/searchList.js +52 -0
- package/open-sse/rtk/filters/smartTruncate.js +15 -0
- package/open-sse/rtk/filters/tree.js +32 -0
- package/open-sse/rtk/index.js +155 -0
- package/open-sse/rtk/registry.js +38 -0
- package/open-sse/services/accountFallback.js +238 -0
- package/open-sse/services/combo.js +198 -0
- package/open-sse/services/compact.js +71 -0
- package/open-sse/services/kiroModels.js +332 -0
- package/open-sse/services/model.js +261 -0
- package/open-sse/services/oauthCredentialManager.js +151 -0
- package/open-sse/services/projectId.js +306 -0
- package/open-sse/services/provider.js +356 -0
- package/open-sse/services/qoderModels.js +214 -0
- package/open-sse/services/tokenRefresh.js +939 -0
- package/open-sse/services/usage.js +1496 -0
- package/open-sse/transformer/responsesTransformer.js +439 -0
- package/open-sse/transformer/streamToJsonConverter.js +103 -0
- package/open-sse/translator/formats.js +36 -0
- package/open-sse/translator/helpers/claudeHelper.js +216 -0
- package/open-sse/translator/helpers/geminiHelper.js +372 -0
- package/open-sse/translator/helpers/imageHelper.js +34 -0
- package/open-sse/translator/helpers/maxTokensHelper.js +27 -0
- package/open-sse/translator/helpers/openaiHelper.js +130 -0
- package/open-sse/translator/helpers/responsesApiHelper.js +139 -0
- package/open-sse/translator/helpers/toolCallHelper.js +148 -0
- package/open-sse/translator/index.js +251 -0
- package/open-sse/translator/request/antigravity-to-openai.js +229 -0
- package/open-sse/translator/request/claude-to-openai.js +232 -0
- package/open-sse/translator/request/gemini-to-openai.js +147 -0
- package/open-sse/translator/request/openai-responses.js +318 -0
- package/open-sse/translator/request/openai-to-claude.js +401 -0
- package/open-sse/translator/request/openai-to-commandcode.js +170 -0
- package/open-sse/translator/request/openai-to-cursor.js +183 -0
- package/open-sse/translator/request/openai-to-gemini.js +470 -0
- package/open-sse/translator/request/openai-to-kiro.js +629 -0
- package/open-sse/translator/request/openai-to-kiro.old.js +278 -0
- package/open-sse/translator/request/openai-to-ollama.js +192 -0
- package/open-sse/translator/request/openai-to-vertex.js +42 -0
- package/open-sse/translator/response/claude-to-openai.js +206 -0
- package/open-sse/translator/response/commandcode-to-openai.js +197 -0
- package/open-sse/translator/response/cursor-to-openai.js +30 -0
- package/open-sse/translator/response/gemini-to-openai.js +245 -0
- package/open-sse/translator/response/kiro-to-openai.js +195 -0
- package/open-sse/translator/response/ollama-to-openai.js +152 -0
- package/open-sse/translator/response/openai-responses.js +590 -0
- package/open-sse/translator/response/openai-to-antigravity.js +122 -0
- package/open-sse/translator/response/openai-to-claude.js +266 -0
- package/open-sse/utils/bypassHandler.js +298 -0
- package/open-sse/utils/claudeCloaking.js +155 -0
- package/open-sse/utils/claudeHeaderCache.js +70 -0
- package/open-sse/utils/clientDetector.js +63 -0
- package/open-sse/utils/cursorChecksum.js +149 -0
- package/open-sse/utils/cursorProtobuf.js +904 -0
- package/open-sse/utils/debugLog.js +14 -0
- package/open-sse/utils/error.js +147 -0
- package/open-sse/utils/ollamaTransform.js +85 -0
- package/open-sse/utils/proxyFetch.js +368 -0
- package/open-sse/utils/reasoningContentInjector.js +79 -0
- package/open-sse/utils/requestLogger.js +260 -0
- package/open-sse/utils/responsesStreamHelpers.js +49 -0
- package/open-sse/utils/sessionManager.js +82 -0
- package/open-sse/utils/stream.js +462 -0
- package/open-sse/utils/streamHandler.js +250 -0
- package/open-sse/utils/streamHelpers.js +122 -0
- package/open-sse/utils/toolDeduper.js +49 -0
- package/open-sse/utils/usageTracking.js +347 -0
- package/package.json +100 -0
- package/postcss.config.mjs +12 -0
- package/public/favicon.svg +11 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/i18n/literals/ar.json +195 -0
- package/public/i18n/literals/bn.json +195 -0
- package/public/i18n/literals/cs.json +195 -0
- package/public/i18n/literals/da.json +195 -0
- package/public/i18n/literals/de.json +195 -0
- package/public/i18n/literals/el.json +195 -0
- package/public/i18n/literals/es.json +195 -0
- package/public/i18n/literals/fi.json +195 -0
- package/public/i18n/literals/fr.json +195 -0
- package/public/i18n/literals/he.json +195 -0
- package/public/i18n/literals/hi.json +195 -0
- package/public/i18n/literals/hu.json +195 -0
- package/public/i18n/literals/id.json +195 -0
- package/public/i18n/literals/it.json +195 -0
- package/public/i18n/literals/ja.json +195 -0
- package/public/i18n/literals/ko.json +195 -0
- package/public/i18n/literals/nl.json +195 -0
- package/public/i18n/literals/no.json +195 -0
- package/public/i18n/literals/pl.json +195 -0
- package/public/i18n/literals/pt-BR.json +195 -0
- package/public/i18n/literals/pt-PT.json +195 -0
- package/public/i18n/literals/ro.json +195 -0
- package/public/i18n/literals/ru.json +195 -0
- package/public/i18n/literals/sv.json +195 -0
- package/public/i18n/literals/th.json +195 -0
- package/public/i18n/literals/tl.json +195 -0
- package/public/i18n/literals/tr.json +195 -0
- package/public/i18n/literals/uk.json +195 -0
- package/public/i18n/literals/ur.json +195 -0
- package/public/i18n/literals/vi.json +195 -0
- package/public/i18n/literals/zh-CN.json +772 -0
- package/public/i18n/literals/zh-TW.json +195 -0
- package/public/icons/discord.svg +4 -0
- package/public/icons/icon-192.svg +4 -0
- package/public/icons/icon-512.svg +4 -0
- package/public/next.svg +1 -0
- package/public/providers/alicode-intl.png +0 -0
- package/public/providers/alicode.png +0 -0
- package/public/providers/amp.png +0 -0
- package/public/providers/anthropic-m.png +0 -0
- package/public/providers/anthropic.png +0 -0
- package/public/providers/antigravity.png +0 -0
- package/public/providers/assemblyai.png +0 -0
- package/public/providers/aws-polly.png +0 -0
- package/public/providers/azure.png +0 -0
- package/public/providers/black-forest-labs.png +0 -0
- package/public/providers/blackbox.png +0 -0
- package/public/providers/brave-search.png +0 -0
- package/public/providers/byteplus.png +0 -0
- package/public/providers/cartesia.png +0 -0
- package/public/providers/cerebras.png +0 -0
- package/public/providers/chutes.png +0 -0
- package/public/providers/claude.png +0 -0
- package/public/providers/cline.png +0 -0
- package/public/providers/cloudflare-ai.png +0 -0
- package/public/providers/codebuddy.svg +37 -0
- package/public/providers/codex.png +0 -0
- package/public/providers/cohere.png +0 -0
- package/public/providers/comfyui.png +0 -0
- package/public/providers/commandcode.png +0 -0
- package/public/providers/continue.png +0 -0
- package/public/providers/copilot.png +0 -0
- package/public/providers/coqui.png +0 -0
- package/public/providers/cursor.png +0 -0
- package/public/providers/deepgram.png +0 -0
- package/public/providers/deepseek-tui.png +0 -0
- package/public/providers/deepseek.png +0 -0
- package/public/providers/droid.png +0 -0
- package/public/providers/edge-tts.png +0 -0
- package/public/providers/elevenlabs.png +0 -0
- package/public/providers/exa.png +0 -0
- package/public/providers/fal-ai.png +0 -0
- package/public/providers/firecrawl.png +0 -0
- package/public/providers/fireworks.png +0 -0
- package/public/providers/gemini-cli.png +0 -0
- package/public/providers/gemini.png +0 -0
- package/public/providers/github.png +0 -0
- package/public/providers/glm-cn.png +0 -0
- package/public/providers/glm.png +0 -0
- package/public/providers/google-pse.png +0 -0
- package/public/providers/google-tts.png +0 -0
- package/public/providers/grok-web.png +0 -0
- package/public/providers/groq.png +0 -0
- package/public/providers/hermes.png +0 -0
- package/public/providers/huggingface.png +0 -0
- package/public/providers/hyperbolic.png +0 -0
- package/public/providers/iflow.png +0 -0
- package/public/providers/inworld.png +0 -0
- package/public/providers/jcode.png +0 -0
- package/public/providers/jina-ai.png +0 -0
- package/public/providers/jina-reader.png +0 -0
- package/public/providers/kilocode.png +0 -0
- package/public/providers/kimi-coding.png +0 -0
- package/public/providers/kimi.png +0 -0
- package/public/providers/kiro.png +0 -0
- package/public/providers/linkup.png +0 -0
- package/public/providers/local-device.png +0 -0
- package/public/providers/minimax-cn.png +0 -0
- package/public/providers/minimax.png +0 -0
- package/public/providers/mistral.png +0 -0
- package/public/providers/nanobanana.png +0 -0
- package/public/providers/nebius.png +0 -0
- package/public/providers/nvidia.png +0 -0
- package/public/providers/oai-cc.png +0 -0
- package/public/providers/oai-r.png +0 -0
- package/public/providers/ollama-local.png +0 -0
- package/public/providers/ollama.png +0 -0
- package/public/providers/openai.png +0 -0
- package/public/providers/openclaw.png +0 -0
- package/public/providers/opencode-go.png +0 -0
- package/public/providers/opencode.png +0 -0
- package/public/providers/openrouter.png +0 -0
- package/public/providers/perplexity-web.png +0 -0
- package/public/providers/perplexity.png +0 -0
- package/public/providers/playht.png +0 -0
- package/public/providers/qoder.png +0 -0
- package/public/providers/qwen.png +0 -0
- package/public/providers/recraft.png +0 -0
- package/public/providers/roo.png +0 -0
- package/public/providers/runwayml.png +0 -0
- package/public/providers/sdwebui.png +0 -0
- package/public/providers/searchapi.png +0 -0
- package/public/providers/searxng.png +0 -0
- package/public/providers/serper.png +0 -0
- package/public/providers/siliconflow.png +0 -0
- package/public/providers/stability-ai.png +0 -0
- package/public/providers/tavily.png +0 -0
- package/public/providers/together.png +0 -0
- package/public/providers/topaz.png +0 -0
- package/public/providers/tortoise.png +0 -0
- package/public/providers/vertex-partner.png +0 -0
- package/public/providers/vertex.png +0 -0
- package/public/providers/volcengine-ark.png +0 -0
- package/public/providers/voyage-ai.png +0 -0
- package/public/providers/xai.png +0 -0
- package/public/providers/xiaomi-mimo.png +0 -0
- package/public/providers/xiaomi-tokenplan.png +0 -0
- package/public/providers/youcom.png +0 -0
- package/public/sw.js +22 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/scripts/compact-request-details.mjs +71 -0
- package/scripts/import-codex-gptjson.mjs +342 -0
- package/scripts/start-standalone.mjs +25 -0
- package/scripts/translate-readme.js +201 -0
- package/src/app/(dashboard)/dashboard/automation/page.js +294 -0
- package/src/app/(dashboard)/dashboard/basic-chat/BasicChatPageClient.js +967 -0
- package/src/app/(dashboard)/dashboard/basic-chat/page.js +5 -0
- package/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.js +66 -0
- package/src/app/(dashboard)/dashboard/cli-tools/[toolId]/ToolDetailClient.js +173 -0
- package/src/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js +11 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/AntigravityToolCard.js +481 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ApiKeySelect.js +66 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/BaseUrlSelect.js +174 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ClaudeToolCard.js +390 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ClineToolCard.js +301 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js +458 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/CopilotToolCard.js +323 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/CoworkToolCard.js +640 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/DeepSeekTuiToolCard.js +338 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/DefaultToolCard.js +271 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/DroidToolCard.js +410 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/EndpointPresetControl.js +128 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/HermesToolCard.js +317 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/JcodeToolCard.js +380 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/KiloToolCard.js +275 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/MitmLinkCard.js +40 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/MitmServerCard.js +329 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/MitmToolCard.js +318 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/OpenClawToolCard.js +388 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/OpenCodeToolCard.js +500 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ToolSummaryCard.js +39 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/cliEndpointMatch.js +13 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/index.js +19 -0
- package/src/app/(dashboard)/dashboard/cli-tools/page.js +7 -0
- package/src/app/(dashboard)/dashboard/combos/page.js +612 -0
- package/src/app/(dashboard)/dashboard/console-log/ConsoleLogClient.js +91 -0
- package/src/app/(dashboard)/dashboard/console-log/page.js +8 -0
- package/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js +1555 -0
- package/src/app/(dashboard)/dashboard/endpoint/page.js +7 -0
- package/src/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page.js +1903 -0
- package/src/app/(dashboard)/dashboard/media-providers/[kind]/page.js +289 -0
- package/src/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js +410 -0
- package/src/app/(dashboard)/dashboard/media-providers/web/page.js +209 -0
- package/src/app/(dashboard)/dashboard/mitm/MitmPageClient.js +117 -0
- package/src/app/(dashboard)/dashboard/mitm/page.js +5 -0
- package/src/app/(dashboard)/dashboard/page.js +7 -0
- package/src/app/(dashboard)/dashboard/profile/page.js +1140 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/AddApiKeyModal.js +389 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/AddCustomModelModal.js +125 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/CompatibleModelsSection.js +250 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/ConnectionRow.js +299 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/CooldownTimer.js +42 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/EditCompatibleNodeModal.js +161 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/ModelRow.js +95 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/PassthroughModelsSection.js +183 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/page.js +2053 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/page.new.js +1724 -0
- package/src/app/(dashboard)/dashboard/providers/components/ConnectionsCard.js +497 -0
- package/src/app/(dashboard)/dashboard/providers/components/ModelAvailabilityBadge.js +185 -0
- package/src/app/(dashboard)/dashboard/providers/components/ModelsCard.js +294 -0
- package/src/app/(dashboard)/dashboard/providers/new/page.js +220 -0
- package/src/app/(dashboard)/dashboard/providers/page.js +1365 -0
- package/src/app/(dashboard)/dashboard/proxy-pools/page.js +1092 -0
- package/src/app/(dashboard)/dashboard/quota/page.js +11 -0
- package/src/app/(dashboard)/dashboard/skills/page.js +112 -0
- package/src/app/(dashboard)/dashboard/translator/page.js +303 -0
- package/src/app/(dashboard)/dashboard/usage/components/OverviewCards.js +35 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/ProviderLimitCard.js +185 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/QuotaProgressBar.js +127 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/QuotaTable.js +259 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js +1394 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.js +244 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js +327 -0
- package/src/app/(dashboard)/dashboard/usage/components/RequestDetailsTab.js +433 -0
- package/src/app/(dashboard)/dashboard/usage/components/UsageChart.js +141 -0
- package/src/app/(dashboard)/dashboard/usage/components/UsageTable.js +247 -0
- package/src/app/(dashboard)/dashboard/usage/page.js +75 -0
- package/src/app/(dashboard)/layout.js +6 -0
- package/src/app/api/auth/login/route.js +76 -0
- package/src/app/api/auth/logout/route.js +12 -0
- package/src/app/api/auth/oidc/callback/route.js +87 -0
- package/src/app/api/auth/oidc/start/route.js +52 -0
- package/src/app/api/auth/oidc/test/route.js +84 -0
- package/src/app/api/auth/status/route.js +45 -0
- package/src/app/api/cli-tools/all-statuses/route.js +46 -0
- package/src/app/api/cli-tools/antigravity-mitm/alias/route.js +53 -0
- package/src/app/api/cli-tools/antigravity-mitm/route.js +202 -0
- package/src/app/api/cli-tools/claude-settings/route.js +203 -0
- package/src/app/api/cli-tools/cline-settings/route.js +133 -0
- package/src/app/api/cli-tools/codex-gateway/accounts/route.js +16 -0
- package/src/app/api/cli-tools/codex-settings/route.js +239 -0
- package/src/app/api/cli-tools/copilot-settings/route.js +148 -0
- package/src/app/api/cli-tools/cowork-mcp-registry/route.js +77 -0
- package/src/app/api/cli-tools/cowork-mcp-tools/route.js +95 -0
- package/src/app/api/cli-tools/cowork-settings/route.js +412 -0
- package/src/app/api/cli-tools/deepseek-tui-settings/route.js +164 -0
- package/src/app/api/cli-tools/droid-settings/route.js +213 -0
- package/src/app/api/cli-tools/hermes-settings/route.js +175 -0
- package/src/app/api/cli-tools/jcode-settings/route.js +216 -0
- package/src/app/api/cli-tools/kilo-settings/route.js +131 -0
- package/src/app/api/cli-tools/openclaw-settings/route.js +292 -0
- package/src/app/api/cli-tools/opencode-settings/route.js +259 -0
- package/src/app/api/combos/[id]/route.js +81 -0
- package/src/app/api/combos/route.js +48 -0
- package/src/app/api/health/route.js +15 -0
- package/src/app/api/init/route.js +4 -0
- package/src/app/api/keys/[id]/route.js +58 -0
- package/src/app/api/keys/route.js +42 -0
- package/src/app/api/locale/route.js +30 -0
- package/src/app/api/mcp/[plugin]/message/route.js +21 -0
- package/src/app/api/mcp/[plugin]/sse/route.js +37 -0
- package/src/app/api/media-providers/tts/deepgram/voices/route.js +65 -0
- package/src/app/api/media-providers/tts/elevenlabs/voices/route.js +71 -0
- package/src/app/api/media-providers/tts/inworld/voices/route.js +61 -0
- package/src/app/api/media-providers/tts/minimax/voices/route.js +113 -0
- package/src/app/api/media-providers/tts/voices/route.js +99 -0
- package/src/app/api/models/alias/route.js +53 -0
- package/src/app/api/models/availability/route.js +103 -0
- package/src/app/api/models/custom/route.js +48 -0
- package/src/app/api/models/disabled/route.js +50 -0
- package/src/app/api/models/route.js +64 -0
- package/src/app/api/models/test/ping.js +191 -0
- package/src/app/api/models/test/route.js +14 -0
- package/src/app/api/oauth/[provider]/[action]/route.js +343 -0
- package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/cancel/route.js +19 -0
- package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/manual/[workerId]/route.js +30 -0
- package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/route.js +23 -0
- package/src/app/api/oauth/codebuddy/bulk-import/latest/route.js +25 -0
- package/src/app/api/oauth/codebuddy/bulk-import/route.js +49 -0
- package/src/app/api/oauth/codebuddy/quota-cookie/route.js +133 -0
- package/src/app/api/oauth/codex/import-token/route.js +96 -0
- package/src/app/api/oauth/cursor/auto-import/route.js +258 -0
- package/src/app/api/oauth/cursor/import/route.js +100 -0
- package/src/app/api/oauth/gitlab/pat/route.js +62 -0
- package/src/app/api/oauth/iflow/cookie/route.js +137 -0
- package/src/app/api/oauth/kiro/auto-import/route.js +85 -0
- package/src/app/api/oauth/kiro/bulk-import/[jobId]/cancel/route.js +18 -0
- package/src/app/api/oauth/kiro/bulk-import/[jobId]/manual/[workerId]/route.js +29 -0
- package/src/app/api/oauth/kiro/bulk-import/[jobId]/route.js +22 -0
- package/src/app/api/oauth/kiro/bulk-import/latest/route.js +25 -0
- package/src/app/api/oauth/kiro/bulk-import/route.js +49 -0
- package/src/app/api/oauth/kiro/import/route.js +110 -0
- package/src/app/api/oauth/kiro/social-authorize/route.js +27 -0
- package/src/app/api/oauth/kiro/social-exchange/route.js +41 -0
- package/src/app/api/pricing/route.js +134 -0
- package/src/app/api/provider-nodes/[id]/route.js +101 -0
- package/src/app/api/provider-nodes/route.js +104 -0
- package/src/app/api/provider-nodes/validate/route.js +201 -0
- package/src/app/api/providers/[id]/models/route.js +526 -0
- package/src/app/api/providers/[id]/route.js +189 -0
- package/src/app/api/providers/[id]/test/route.js +23 -0
- package/src/app/api/providers/[id]/test/testUtils.js +714 -0
- package/src/app/api/providers/[id]/test-models/route.js +66 -0
- package/src/app/api/providers/client/route.js +126 -0
- package/src/app/api/providers/kilo/free-models/route.js +55 -0
- package/src/app/api/providers/route.js +206 -0
- package/src/app/api/providers/suggested-models/filters.js +20 -0
- package/src/app/api/providers/suggested-models/route.js +32 -0
- package/src/app/api/providers/test-batch/route.js +131 -0
- package/src/app/api/providers/validate/route.js +637 -0
- package/src/app/api/proxy-pools/[id]/route.js +123 -0
- package/src/app/api/proxy-pools/[id]/test/route.js +70 -0
- package/src/app/api/proxy-pools/cloudflare-deploy/route.js +145 -0
- package/src/app/api/proxy-pools/deno-deploy/route.js +175 -0
- package/src/app/api/proxy-pools/route.js +93 -0
- package/src/app/api/proxy-pools/vercel-deploy/route.js +142 -0
- package/src/app/api/settings/database/route.js +36 -0
- package/src/app/api/settings/proxy-test/route.js +23 -0
- package/src/app/api/settings/require-login/route.js +15 -0
- package/src/app/api/settings/route.js +100 -0
- package/src/app/api/shutdown/route.js +24 -0
- package/src/app/api/tags/route.js +18 -0
- package/src/app/api/translator/console-logs/route.js +24 -0
- package/src/app/api/translator/console-logs/stream/route.js +79 -0
- package/src/app/api/translator/load/route.js +45 -0
- package/src/app/api/translator/save/route.js +44 -0
- package/src/app/api/translator/send/route.js +94 -0
- package/src/app/api/translator/translate/route.js +90 -0
- package/src/app/api/tunnel/disable/route.js +12 -0
- package/src/app/api/tunnel/enable/route.js +16 -0
- package/src/app/api/tunnel/status/route.js +13 -0
- package/src/app/api/tunnel/tailscale-check/route.js +50 -0
- package/src/app/api/tunnel/tailscale-disable/route.js +12 -0
- package/src/app/api/tunnel/tailscale-enable/route.js +12 -0
- package/src/app/api/tunnel/tailscale-install/route.js +72 -0
- package/src/app/api/usage/[connectionId]/route.js +188 -0
- package/src/app/api/usage/chart/route.js +21 -0
- package/src/app/api/usage/history/route.js +12 -0
- package/src/app/api/usage/logs/route.js +12 -0
- package/src/app/api/usage/providers/route.js +42 -0
- package/src/app/api/usage/request-details/route.js +57 -0
- package/src/app/api/usage/request-logs/route.js +13 -0
- package/src/app/api/usage/stats/route.js +23 -0
- package/src/app/api/usage/stream/route.js +79 -0
- package/src/app/api/v1/api/chat/route.js +37 -0
- package/src/app/api/v1/audio/speech/route.js +16 -0
- package/src/app/api/v1/audio/transcriptions/route.js +19 -0
- package/src/app/api/v1/audio/voices/route.js +68 -0
- package/src/app/api/v1/chat/completions/route.js +35 -0
- package/src/app/api/v1/embeddings/route.js +21 -0
- package/src/app/api/v1/images/generations/route.js +16 -0
- package/src/app/api/v1/messages/count_tokens/route.js +52 -0
- package/src/app/api/v1/messages/route.js +36 -0
- package/src/app/api/v1/models/[kind]/route.js +55 -0
- package/src/app/api/v1/models/info/route.js +110 -0
- package/src/app/api/v1/models/route.js +451 -0
- package/src/app/api/v1/responses/compact/route.js +37 -0
- package/src/app/api/v1/responses/route.js +30 -0
- package/src/app/api/v1/route.js +1 -0
- package/src/app/api/v1/search/route.js +21 -0
- package/src/app/api/v1/web/fetch/route.js +21 -0
- package/src/app/api/v1beta/models/[...path]/route.js +328 -0
- package/src/app/api/v1beta/models/route.js +44 -0
- package/src/app/api/version/route.js +45 -0
- package/src/app/api/version/shutdown/route.js +15 -0
- package/src/app/api/version/update/route.js +21 -0
- package/src/app/callback/page.js +148 -0
- package/src/app/dashboard/settings/pricing/page.js +173 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +496 -0
- package/src/app/landing/components/AnimatedBackground.js +57 -0
- package/src/app/landing/components/Features.js +133 -0
- package/src/app/landing/components/FlowAnimation.js +175 -0
- package/src/app/landing/components/Footer.js +61 -0
- package/src/app/landing/components/GetStarted.js +97 -0
- package/src/app/landing/components/HeroSection.js +47 -0
- package/src/app/landing/components/HowItWorks.js +66 -0
- package/src/app/landing/components/Navigation.js +72 -0
- package/src/app/landing/page.js +106 -0
- package/src/app/layout.js +49 -0
- package/src/app/login/page.js +197 -0
- package/src/app/manifest.js +30 -0
- package/src/app/page.js +5 -0
- package/src/dashboardGuard.js +242 -0
- package/src/i18n/RuntimeI18nProvider.js +27 -0
- package/src/i18n/config.js +146 -0
- package/src/i18n/runtime.js +162 -0
- package/src/lib/appUpdater.js +200 -0
- package/src/lib/auth/dashboardSession.js +68 -0
- package/src/lib/auth/loginLimiter.js +52 -0
- package/src/lib/auth/oidc.js +234 -0
- package/src/lib/consoleLogBuffer.js +79 -0
- package/src/lib/dataDir.js +29 -0
- package/src/lib/db/adapters/betterSqliteAdapter.js +55 -0
- package/src/lib/db/adapters/bunSqliteAdapter.js +63 -0
- package/src/lib/db/adapters/nodeSqliteAdapter.js +84 -0
- package/src/lib/db/adapters/sqljsAdapter.js +115 -0
- package/src/lib/db/backup.js +35 -0
- package/src/lib/db/driver.js +85 -0
- package/src/lib/db/helpers/jsonCol.js +9 -0
- package/src/lib/db/helpers/kvStore.js +39 -0
- package/src/lib/db/helpers/metaStore.js +22 -0
- package/src/lib/db/index.js +171 -0
- package/src/lib/db/migrate.js +286 -0
- package/src/lib/db/migrations/001-initial.js +14 -0
- package/src/lib/db/migrations/index.js +10 -0
- package/src/lib/db/paths.js +18 -0
- package/src/lib/db/repos/aliasRepo.js +62 -0
- package/src/lib/db/repos/apiKeysRepo.js +75 -0
- package/src/lib/db/repos/combosRepo.js +73 -0
- package/src/lib/db/repos/connectionsRepo.js +226 -0
- package/src/lib/db/repos/disabledModelsRepo.js +56 -0
- package/src/lib/db/repos/nodesRepo.js +95 -0
- package/src/lib/db/repos/pricingRepo.js +108 -0
- package/src/lib/db/repos/proxyPoolsRepo.js +103 -0
- package/src/lib/db/repos/requestDetailsRepo.js +259 -0
- package/src/lib/db/repos/settingsRepo.js +104 -0
- package/src/lib/db/repos/usageRepo.js +731 -0
- package/src/lib/db/schema.js +157 -0
- package/src/lib/db/version.js +21 -0
- package/src/lib/disabledModelsDb.js +4 -0
- package/src/lib/localDb.js +21 -0
- package/src/lib/mcp/stdioSseBridge.js +198 -0
- package/src/lib/mitmAliasCache.js +46 -0
- package/src/lib/network/connectionProxy.js +160 -0
- package/src/lib/network/initOutboundProxy.js +25 -0
- package/src/lib/network/outboundProxy.js +68 -0
- package/src/lib/network/proxyTest.js +91 -0
- package/src/lib/oauth/constants/oauth.js +284 -0
- package/src/lib/oauth/constants/xai.js +61 -0
- package/src/lib/oauth/providers.js +1506 -0
- package/src/lib/oauth/services/antigravity.js +321 -0
- package/src/lib/oauth/services/claude.js +136 -0
- package/src/lib/oauth/services/codebuddyBulkImportManager.js +454 -0
- package/src/lib/oauth/services/codex.js +144 -0
- package/src/lib/oauth/services/cursor.js +179 -0
- package/src/lib/oauth/services/gemini.js +240 -0
- package/src/lib/oauth/services/github.js +225 -0
- package/src/lib/oauth/services/iflow.js +202 -0
- package/src/lib/oauth/services/index.js +17 -0
- package/src/lib/oauth/services/kiro.js +334 -0
- package/src/lib/oauth/services/kiroBulkImportManager.js +778 -0
- package/src/lib/oauth/services/kiroConnections.js +93 -0
- package/src/lib/oauth/services/kiroGoogleAutomation.js +1136 -0
- package/src/lib/oauth/services/oauth.js +157 -0
- package/src/lib/oauth/services/openai.js +123 -0
- package/src/lib/oauth/services/qoder.js +216 -0
- package/src/lib/oauth/services/qwen.js +170 -0
- package/src/lib/oauth/services/xai.js +238 -0
- package/src/lib/oauth/utils/banner.js +63 -0
- package/src/lib/oauth/utils/pkce.js +39 -0
- package/src/lib/oauth/utils/server.js +415 -0
- package/src/lib/oauth/utils/ui.js +48 -0
- package/src/lib/providerNormalization.js +45 -0
- package/src/lib/qoder/constants.js +64 -0
- package/src/lib/qoder/cosy.js +175 -0
- package/src/lib/qoder/encoding.js +55 -0
- package/src/lib/requestDetailsDb.js +4 -0
- package/src/lib/tunnel/cloudflare/cloudflared.js +449 -0
- package/src/lib/tunnel/cloudflare/config.js +9 -0
- package/src/lib/tunnel/cloudflare/healthCheck.js +29 -0
- package/src/lib/tunnel/cloudflare/manager.js +151 -0
- package/src/lib/tunnel/cloudflare/pid.js +23 -0
- package/src/lib/tunnel/index.js +48 -0
- package/src/lib/tunnel/shared/dnsResolver.js +17 -0
- package/src/lib/tunnel/shared/internetCheck.js +26 -0
- package/src/lib/tunnel/shared/state.js +41 -0
- package/src/lib/tunnel/shared/watchdogConfig.js +8 -0
- package/src/lib/tunnel/tailscale/config.js +7 -0
- package/src/lib/tunnel/tailscale/healthCheck.js +29 -0
- package/src/lib/tunnel/tailscale/manager.js +129 -0
- package/src/lib/tunnel/tailscale/tailscale.js +790 -0
- package/src/lib/updater/updater.js +235 -0
- package/src/lib/usage/fetcher.js +208 -0
- package/src/lib/usageDb.js +7 -0
- package/src/mitm/antigravityIdeVersion.js +50 -0
- package/src/mitm/cert/generate.js +32 -0
- package/src/mitm/cert/install.js +269 -0
- package/src/mitm/cert/rootCA.js +173 -0
- package/src/mitm/config.js +87 -0
- package/src/mitm/dbReader.js +22 -0
- package/src/mitm/dns/dnsConfig.js +266 -0
- package/src/mitm/handlers/antigravity.js +33 -0
- package/src/mitm/handlers/base.js +226 -0
- package/src/mitm/handlers/copilot.js +35 -0
- package/src/mitm/handlers/cursor.js +15 -0
- package/src/mitm/handlers/kiro.js +526 -0
- package/src/mitm/logger.js +106 -0
- package/src/mitm/manager.js +851 -0
- package/src/mitm/paths.js +32 -0
- package/src/mitm/server.js +435 -0
- package/src/mitm/winElevated.js +81 -0
- package/src/models/index.js +38 -0
- package/src/proxy.js +5 -0
- package/src/shared/components/AddCustomEmbeddingModal.js +183 -0
- package/src/shared/components/Avatar.js +88 -0
- package/src/shared/components/Badge.js +54 -0
- package/src/shared/components/BulkAccountAutomationModal.js +508 -0
- package/src/shared/components/Button.js +56 -0
- package/src/shared/components/Card.js +116 -0
- package/src/shared/components/ChangelogModal.js +97 -0
- package/src/shared/components/CodeBuddyQuotaCookieModal.js +109 -0
- package/src/shared/components/ComboFormModal.js +176 -0
- package/src/shared/components/CursorAuthModal.js +212 -0
- package/src/shared/components/DonateModal.js +136 -0
- package/src/shared/components/Drawer.js +82 -0
- package/src/shared/components/EditConnectionModal.js +286 -0
- package/src/shared/components/Footer.js +132 -0
- package/src/shared/components/GitLabAuthModal.js +194 -0
- package/src/shared/components/Header.js +380 -0
- package/src/shared/components/HeaderLanguage.js +46 -0
- package/src/shared/components/HeaderMenu.js +126 -0
- package/src/shared/components/IFlowCookieModal.js +132 -0
- package/src/shared/components/Input.js +65 -0
- package/src/shared/components/KiroAuthModal.js +1171 -0
- package/src/shared/components/KiroOAuthWrapper.js +149 -0
- package/src/shared/components/KiroSocialOAuthModal.js +205 -0
- package/src/shared/components/LanguageSwitcher.js +190 -0
- package/src/shared/components/Loading.js +75 -0
- package/src/shared/components/ManualConfigModal.js +44 -0
- package/src/shared/components/McpMarketplaceModal.js +255 -0
- package/src/shared/components/Modal.js +146 -0
- package/src/shared/components/ModelSelectModal.js +537 -0
- package/src/shared/components/NineRemoteButton.js +23 -0
- package/src/shared/components/NineRemotePromoModal.js +99 -0
- package/src/shared/components/NoAuthProxyCard.js +86 -0
- package/src/shared/components/OAuthModal.js +682 -0
- package/src/shared/components/Pagination.js +150 -0
- package/src/shared/components/PricingModal.js +208 -0
- package/src/shared/components/ProviderIcon.js +63 -0
- package/src/shared/components/ProviderInfoCard.js +82 -0
- package/src/shared/components/RequestLogger.js +121 -0
- package/src/shared/components/SegmentedControl.js +48 -0
- package/src/shared/components/Select.js +67 -0
- package/src/shared/components/Sidebar.js +441 -0
- package/src/shared/components/ThemeProvider.js +15 -0
- package/src/shared/components/ThemeToggle.js +42 -0
- package/src/shared/components/Toggle.js +69 -0
- package/src/shared/components/Tooltip.js +25 -0
- package/src/shared/components/UsageStats.js +505 -0
- package/src/shared/components/index.js +46 -0
- package/src/shared/components/layouts/AuthLayout.js +29 -0
- package/src/shared/components/layouts/DashboardLayout.js +104 -0
- package/src/shared/components/layouts/index.js +4 -0
- package/src/shared/constants/cliTools.js +397 -0
- package/src/shared/constants/colors.js +77 -0
- package/src/shared/constants/config.js +99 -0
- package/src/shared/constants/coworkPlugins.js +75 -0
- package/src/shared/constants/index.js +4 -0
- package/src/shared/constants/locales.js +36 -0
- package/src/shared/constants/mitmToolHosts.js +12 -0
- package/src/shared/constants/models.js +38 -0
- package/src/shared/constants/pricing.js +303 -0
- package/src/shared/constants/providers.js +289 -0
- package/src/shared/constants/skills.js +78 -0
- package/src/shared/constants/ttsProviders.js +138 -0
- package/src/shared/hooks/index.js +2 -0
- package/src/shared/hooks/useCopyToClipboard.js +43 -0
- package/src/shared/hooks/useTheme.js +60 -0
- package/src/shared/services/bootstrap.js +12 -0
- package/src/shared/services/initializeApp.js +268 -0
- package/src/shared/utils/api.js +93 -0
- package/src/shared/utils/apiKey.js +98 -0
- package/src/shared/utils/clineAuth.js +37 -0
- package/src/shared/utils/cn.js +11 -0
- package/src/shared/utils/connectionStatus.js +162 -0
- package/src/shared/utils/index.js +40 -0
- package/src/shared/utils/machine.js +6 -0
- package/src/shared/utils/machineId.js +66 -0
- package/src/shared/utils/providerModelsFetcher.js +30 -0
- package/src/sse/handlers/chat.js +261 -0
- package/src/sse/handlers/embeddings.js +141 -0
- package/src/sse/handlers/fetch.js +213 -0
- package/src/sse/handlers/imageGeneration.js +142 -0
- package/src/sse/handlers/search.js +206 -0
- package/src/sse/handlers/stt.js +88 -0
- package/src/sse/handlers/tts.js +114 -0
- package/src/sse/services/auth.js +346 -0
- package/src/sse/services/codexGateway.js +215 -0
- package/src/sse/services/model.js +99 -0
- package/src/sse/services/tokenRefresh.js +319 -0
- package/src/sse/utils/logger.js +75 -0
- package/src/store/headerSearchStore.js +19 -0
- package/src/store/index.js +6 -0
- package/src/store/notificationStore.js +45 -0
- package/src/store/providerStore.js +55 -0
- package/src/store/settingsStore.js +51 -0
- package/src/store/themeStore.js +54 -0
- package/src/store/userStore.js +20 -0
- package/start.sh +4 -0
|
@@ -0,0 +1,1136 @@
|
|
|
1
|
+
const DEFAULT_SHORT_TIMEOUT_MS = 90_000;
|
|
2
|
+
const DEFAULT_MANUAL_TIMEOUT_MS = 15 * 60_000;
|
|
3
|
+
|
|
4
|
+
const EMAIL_INPUT_SELECTOR = 'input[type="email"], input[autocomplete="username"]';
|
|
5
|
+
const PASSWORD_INPUT_SELECTOR = 'input[type="password"]';
|
|
6
|
+
|
|
7
|
+
const NEXT_BUTTON_SELECTORS = [
|
|
8
|
+
'button:has-text("Next")',
|
|
9
|
+
'button:has-text("Berikutnya")',
|
|
10
|
+
'button:has-text("Continue")',
|
|
11
|
+
'div[role="button"]:has-text("Next")',
|
|
12
|
+
'div[role="button"]:has-text("Berikutnya")',
|
|
13
|
+
'#identifierNext button',
|
|
14
|
+
'#passwordNext button',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const APPROVE_BUTTON_SELECTORS = [
|
|
18
|
+
'#submit_approve_access',
|
|
19
|
+
'#submit_approve_access button',
|
|
20
|
+
'button[jsname]:has-text("Allow")',
|
|
21
|
+
'button:has-text("Allow")',
|
|
22
|
+
'[role="button"]:has-text("Allow")',
|
|
23
|
+
'input[type="submit"][value="Allow"]',
|
|
24
|
+
'input[type="button"][value="Allow"]',
|
|
25
|
+
'button[jsname]:has-text("Izinkan")',
|
|
26
|
+
'button:has-text("Izinkan")',
|
|
27
|
+
'[role="button"]:has-text("Izinkan")',
|
|
28
|
+
'button:has-text("Continue")',
|
|
29
|
+
'button:has-text("Next")',
|
|
30
|
+
'button:has-text("Yes")',
|
|
31
|
+
'button:has-text("Accept")',
|
|
32
|
+
'button:has-text("Lanjutkan")',
|
|
33
|
+
'button:has-text("Berikutnya")',
|
|
34
|
+
'button:has-text("Setuju")',
|
|
35
|
+
'button:has-text("Saya mengerti")',
|
|
36
|
+
'button:has-text("Oke")',
|
|
37
|
+
'button:has-text("OK")',
|
|
38
|
+
'button:has-text("Got it")',
|
|
39
|
+
'button:has-text("I understand")',
|
|
40
|
+
'div[role="button"]:has-text("Continue")',
|
|
41
|
+
'div[role="button"]:has-text("Next")',
|
|
42
|
+
'div[role="button"]:has-text("Allow")',
|
|
43
|
+
'div[role="button"]:has-text("Lanjutkan")',
|
|
44
|
+
'div[role="button"]:has-text("Berikutnya")',
|
|
45
|
+
'div[role="button"]:has-text("Izinkan")',
|
|
46
|
+
'div[role="button"]:has-text("Setuju")',
|
|
47
|
+
'div[role="button"]:has-text("Saya mengerti")',
|
|
48
|
+
'div[role="button"]:has-text("Oke")',
|
|
49
|
+
'div[role="button"]:has-text("OK")',
|
|
50
|
+
'div[role="button"]:has-text("Got it")',
|
|
51
|
+
'div[role="button"]:has-text("I understand")',
|
|
52
|
+
'input[type="button"][value="Saya mengerti"]',
|
|
53
|
+
'input[type="submit"][value="Saya mengerti"]',
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const SKIP_BUTTON_SELECTORS = [
|
|
57
|
+
'button:has-text("Skip")',
|
|
58
|
+
'button:has-text("Lewati")',
|
|
59
|
+
'button:has-text("Not now")',
|
|
60
|
+
'button:has-text("Bukan sekarang")',
|
|
61
|
+
'button:has-text("No thanks")',
|
|
62
|
+
'button:has-text("Tidak sekarang")',
|
|
63
|
+
'div[role="button"]:has-text("Skip")',
|
|
64
|
+
'div[role="button"]:has-text("Not now")',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const GOOGLE_LOGIN_BUTTON_SELECTORS = [
|
|
68
|
+
'#social-google',
|
|
69
|
+
'a#social-google',
|
|
70
|
+
'a:has-text("Sign up with Google")',
|
|
71
|
+
'a:has-text("Log in with Google")',
|
|
72
|
+
'button:has-text("Sign up with Google")',
|
|
73
|
+
'button:has-text("Log in with Google")',
|
|
74
|
+
'button:has-text("Google")',
|
|
75
|
+
'a:has-text("Google")',
|
|
76
|
+
'div[role="button"]:has-text("Google")',
|
|
77
|
+
'span:has-text("Google")',
|
|
78
|
+
'[aria-label*="Google"]',
|
|
79
|
+
'[data-provider*="google" i]',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const TERMS_CHECKBOX_SELECTORS = [
|
|
83
|
+
'#agree-policy-account',
|
|
84
|
+
'#agree-policy',
|
|
85
|
+
'#agree-policy-sso',
|
|
86
|
+
'input[type="checkbox"][id*="agree" i]',
|
|
87
|
+
'input[type="checkbox"][name*="agree" i]',
|
|
88
|
+
'input[type="checkbox"][id*="policy" i]',
|
|
89
|
+
'input[type="checkbox"][name*="policy" i]',
|
|
90
|
+
'input[type="checkbox"][id*="terms" i]',
|
|
91
|
+
'input[type="checkbox"][name*="terms" i]',
|
|
92
|
+
'.login-checkbox input[type="checkbox"]',
|
|
93
|
+
'[class*="checkbox"] input[type="checkbox"]',
|
|
94
|
+
'[class*="agree"] input[type="checkbox"]',
|
|
95
|
+
'input[type="checkbox"]',
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const PRIVACY_CONFIRM_BUTTON_SELECTORS = [
|
|
99
|
+
'.ui-dialog button:has-text("Confirm")',
|
|
100
|
+
'dialog button:has-text("Confirm")',
|
|
101
|
+
'button:has-text("Confirm")',
|
|
102
|
+
'button:has-text("I agree")',
|
|
103
|
+
'button:has-text("Agree")',
|
|
104
|
+
'button:has-text("同意")',
|
|
105
|
+
'button:has-text("确认")',
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
const PROVIDER_ONBOARDING_ACTION_SELECTORS = [
|
|
109
|
+
'button:has-text("Continue")',
|
|
110
|
+
'[role="button"]:has-text("Continue")',
|
|
111
|
+
'button:has-text("Get started")',
|
|
112
|
+
'button:has-text("GET STARTED")',
|
|
113
|
+
'input[type="submit"][value="GET STARTED"]',
|
|
114
|
+
'button:has-text("Start")',
|
|
115
|
+
'button:has-text("Confirm")',
|
|
116
|
+
'button:has-text("Done")',
|
|
117
|
+
'button:has-text("Next")',
|
|
118
|
+
'button:has-text("Skip")',
|
|
119
|
+
'button:has-text("Not now")',
|
|
120
|
+
'button:has-text("Save")',
|
|
121
|
+
'button:has-text("Create")',
|
|
122
|
+
'button:has-text("Enter")',
|
|
123
|
+
'button:has-text("Launch")',
|
|
124
|
+
'button:has-text("Use CodeBuddy")',
|
|
125
|
+
'button:has-text("Go to CodeBuddy")',
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
const PROVIDER_REGION_TRIGGER_SELECTORS = [
|
|
129
|
+
'select',
|
|
130
|
+
'[role="combobox"]',
|
|
131
|
+
'.page-region [role="combobox"]',
|
|
132
|
+
'.page-region .t-select',
|
|
133
|
+
'.page-region [class*="t-select"]',
|
|
134
|
+
'.page-region [class*="select"]',
|
|
135
|
+
'.page-region input[placeholder]',
|
|
136
|
+
'button:has-text("Region")',
|
|
137
|
+
'[role="button"]:has-text("Region")',
|
|
138
|
+
'button:has-text("Select region")',
|
|
139
|
+
'[role="button"]:has-text("Select region")',
|
|
140
|
+
'button:has-text("Data region")',
|
|
141
|
+
'[aria-label*="region" i]',
|
|
142
|
+
'[placeholder*="region" i]',
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const PROVIDER_REGION_OPTION_SELECTORS = [
|
|
146
|
+
'text=/^Indonesia$/i',
|
|
147
|
+
'text=/^ID$/i',
|
|
148
|
+
'text=/^Singapore$/i',
|
|
149
|
+
'text=/^SG$/i',
|
|
150
|
+
'text=/^Japan$/i',
|
|
151
|
+
'text=/^JP$/i',
|
|
152
|
+
'text=/^Thailand$/i',
|
|
153
|
+
'text=/^TH$/i',
|
|
154
|
+
'text=/^Global$/i',
|
|
155
|
+
'text=/^International$/i',
|
|
156
|
+
'text=/^United States$/i',
|
|
157
|
+
'text=/^US$/i',
|
|
158
|
+
'text=/^Asia Pacific$/i',
|
|
159
|
+
'text=/^Hong Kong$/i',
|
|
160
|
+
'text=/^Default$/i',
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
const PROVIDER_ONBOARDING_INPUT_DEFAULTS = [
|
|
164
|
+
{ selector: 'input[name*="workspace" i]', value: "Default" },
|
|
165
|
+
{ selector: 'input[placeholder*="workspace" i]', value: "Default" },
|
|
166
|
+
{ selector: 'input[name*="team" i]', value: "Default" },
|
|
167
|
+
{ selector: 'input[placeholder*="team" i]', value: "Default" },
|
|
168
|
+
{ selector: 'input[name*="name" i]', value: "Default" },
|
|
169
|
+
{ selector: 'input[placeholder*="name" i]', value: "Default" },
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
const INVALID_CREDENTIAL_MARKERS = [
|
|
173
|
+
"wrong password",
|
|
174
|
+
"incorrect password",
|
|
175
|
+
"couldn't find your google account",
|
|
176
|
+
"couldn’t find your google account",
|
|
177
|
+
"enter a valid email",
|
|
178
|
+
"couldn’t sign you in",
|
|
179
|
+
"couldn't sign you in",
|
|
180
|
+
"invalid email or password",
|
|
181
|
+
"password is incorrect",
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
const MANUAL_ASSIST_MARKERS = [
|
|
185
|
+
"2-step verification",
|
|
186
|
+
"2-step verification required",
|
|
187
|
+
"verify it’s you",
|
|
188
|
+
"verify it's you",
|
|
189
|
+
"check your phone",
|
|
190
|
+
"confirm it’s you",
|
|
191
|
+
"confirm it's you",
|
|
192
|
+
"recovery email",
|
|
193
|
+
"recovery phone",
|
|
194
|
+
"suspicious sign-in prevented",
|
|
195
|
+
"unusual activity detected",
|
|
196
|
+
"captcha",
|
|
197
|
+
"try again later",
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
const GOOGLE_ONBOARDING_MARKERS = [
|
|
201
|
+
"welcome to your new google account",
|
|
202
|
+
"selamat datang di akun google baru anda",
|
|
203
|
+
"welcome to your new account",
|
|
204
|
+
"selamat datang di akun baru",
|
|
205
|
+
"privacy and terms",
|
|
206
|
+
"privasi dan persyaratan",
|
|
207
|
+
"personalize your google services",
|
|
208
|
+
"personalisasikan layanan google anda",
|
|
209
|
+
"add recovery phone",
|
|
210
|
+
"tambahkan nomor telepon pemulihan",
|
|
211
|
+
"choose your settings",
|
|
212
|
+
"pilih setelan anda",
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
const KIRO_CALLBACK_PREFIX = "kiro://kiro.kiroAgent/authenticate-success";
|
|
216
|
+
|
|
217
|
+
function parseCallbackUrl(rawUrl) {
|
|
218
|
+
if (!rawUrl || !rawUrl.startsWith(KIRO_CALLBACK_PREFIX)) return null;
|
|
219
|
+
|
|
220
|
+
const queryIndex = rawUrl.indexOf("?");
|
|
221
|
+
const params = new URLSearchParams(queryIndex >= 0 ? rawUrl.slice(queryIndex + 1) : "");
|
|
222
|
+
const code = params.get("code");
|
|
223
|
+
const state = params.get("state");
|
|
224
|
+
|
|
225
|
+
if (!code) return null;
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
callbackUrl: rawUrl,
|
|
229
|
+
code,
|
|
230
|
+
state,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getInteractionScopes(page) {
|
|
235
|
+
const frames = typeof page.frames === "function" ? page.frames() : [];
|
|
236
|
+
return [page, ...frames.filter((frame) => frame !== page.mainFrame?.())];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function clickFirstVisible(page, selectors) {
|
|
240
|
+
for (const scope of getInteractionScopes(page)) {
|
|
241
|
+
for (const selector of selectors) {
|
|
242
|
+
const locator = scope.locator(selector).first();
|
|
243
|
+
const count = await locator.count().catch(() => 0);
|
|
244
|
+
if (!count) continue;
|
|
245
|
+
|
|
246
|
+
const visible = await locator.isVisible().catch(() => false);
|
|
247
|
+
if (!visible) continue;
|
|
248
|
+
|
|
249
|
+
await locator.click({ timeout: 5_000 }).catch(() => null);
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function clickFirstActionable(page, selectors) {
|
|
258
|
+
for (const scope of getInteractionScopes(page)) {
|
|
259
|
+
for (const selector of selectors) {
|
|
260
|
+
const locator = scope.locator(selector).first();
|
|
261
|
+
const count = await locator.count().catch(() => 0);
|
|
262
|
+
if (!count) continue;
|
|
263
|
+
|
|
264
|
+
await locator.scrollIntoViewIfNeeded().catch(() => null);
|
|
265
|
+
|
|
266
|
+
const visible = await locator.isVisible().catch(() => false);
|
|
267
|
+
if (!visible) continue;
|
|
268
|
+
|
|
269
|
+
const enabled = await locator.isEnabled().catch(() => true);
|
|
270
|
+
if (!enabled) continue;
|
|
271
|
+
|
|
272
|
+
const clicked = await locator.click({ timeout: 5_000 }).then(() => true).catch(() => false);
|
|
273
|
+
if (clicked) return true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function checkFirstVisible(page, selectors) {
|
|
281
|
+
for (const scope of getInteractionScopes(page)) {
|
|
282
|
+
for (const selector of selectors) {
|
|
283
|
+
const locator = scope.locator(selector).first();
|
|
284
|
+
const count = await locator.count().catch(() => 0);
|
|
285
|
+
if (!count) continue;
|
|
286
|
+
|
|
287
|
+
const checked = await locator.isChecked().catch(() => false);
|
|
288
|
+
if (checked) return true;
|
|
289
|
+
|
|
290
|
+
const visible = await locator.isVisible().catch(() => false);
|
|
291
|
+
const didCheck = visible
|
|
292
|
+
? await locator.check({ force: true, timeout: 5_000 }).then(() => true).catch(() => false)
|
|
293
|
+
: false;
|
|
294
|
+
if (didCheck) return true;
|
|
295
|
+
|
|
296
|
+
const clicked = visible
|
|
297
|
+
? await locator.click({ force: true, timeout: 5_000 }).then(() => true).catch(() => false)
|
|
298
|
+
: false;
|
|
299
|
+
if (clicked) return true;
|
|
300
|
+
|
|
301
|
+
const domChecked = await scope.evaluate((candidateSelector) => {
|
|
302
|
+
const input = document.querySelector(candidateSelector);
|
|
303
|
+
if (!(input instanceof HTMLInputElement)) return false;
|
|
304
|
+
input.checked = true;
|
|
305
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
306
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
307
|
+
return input.checked;
|
|
308
|
+
}, selector).catch(() => false);
|
|
309
|
+
if (domChecked) return true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function getFirstVisibleLocator(page, selector) {
|
|
317
|
+
for (const scope of getInteractionScopes(page)) {
|
|
318
|
+
const locator = scope.locator(selector).first();
|
|
319
|
+
const count = await locator.count().catch(() => 0);
|
|
320
|
+
if (!count) continue;
|
|
321
|
+
|
|
322
|
+
const visible = await locator.isVisible().catch(() => false);
|
|
323
|
+
if (!visible) continue;
|
|
324
|
+
|
|
325
|
+
return locator;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function readPageText(page) {
|
|
332
|
+
const chunks = [];
|
|
333
|
+
for (const scope of getInteractionScopes(page)) {
|
|
334
|
+
try {
|
|
335
|
+
chunks.push(await scope.evaluate(() => document.body?.innerText || ""));
|
|
336
|
+
} catch {
|
|
337
|
+
// Cross-origin frames can be unreadable; ignore them.
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return chunks.join("\n");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function includesAny(text, markers) {
|
|
344
|
+
const normalized = String(text || "").toLowerCase();
|
|
345
|
+
return markers.some((marker) => normalized.includes(marker));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function isGoogleAuthPage(page) {
|
|
349
|
+
try {
|
|
350
|
+
const url = new URL(page.url());
|
|
351
|
+
return url.hostname === "accounts.google.com" || url.hostname.endsWith(".accounts.google.com");
|
|
352
|
+
} catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function isProviderPage(page) {
|
|
358
|
+
try {
|
|
359
|
+
const url = new URL(page.url());
|
|
360
|
+
return /codebuddy\.(ai|cn)$/.test(url.hostname)
|
|
361
|
+
|| url.hostname.endsWith(".codebuddy.ai")
|
|
362
|
+
|| url.hostname.endsWith(".codebuddy.cn");
|
|
363
|
+
} catch {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function handleGoogleConsent(page, reportStep) {
|
|
369
|
+
if (!isGoogleAuthPage(page)) return false;
|
|
370
|
+
|
|
371
|
+
const text = await readPageText(page);
|
|
372
|
+
const looksLikeConsent = /wants to access|ingin mengakses|akses ke akun google|allow/i.test(text);
|
|
373
|
+
if (!looksLikeConsent) return false;
|
|
374
|
+
|
|
375
|
+
await page.evaluate(() => {
|
|
376
|
+
const root = document.scrollingElement || document.documentElement || document.body;
|
|
377
|
+
if (root) root.scrollTop = root.scrollHeight;
|
|
378
|
+
window.scrollTo(0, document.body?.scrollHeight || document.documentElement?.scrollHeight || 0);
|
|
379
|
+
}).catch(() => null);
|
|
380
|
+
await page.waitForTimeout(300);
|
|
381
|
+
|
|
382
|
+
const clickedApprove = await clickFirstActionable(page, APPROVE_BUTTON_SELECTORS);
|
|
383
|
+
if (clickedApprove) {
|
|
384
|
+
reportStep("approving_google_consent", "Approving Google OAuth consent");
|
|
385
|
+
await page.waitForTimeout(1000);
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function handleGoogleOnboarding(page, pageText) {
|
|
393
|
+
const text = String(pageText || "");
|
|
394
|
+
if (!includesAny(text, GOOGLE_ONBOARDING_MARKERS)) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
await page.evaluate(() => {
|
|
399
|
+
const root = document.scrollingElement || document.documentElement || document.body;
|
|
400
|
+
if (root) root.scrollTop = root.scrollHeight;
|
|
401
|
+
window.scrollTo(0, document.body?.scrollHeight || document.documentElement?.scrollHeight || 0);
|
|
402
|
+
}).catch(() => null);
|
|
403
|
+
await page.waitForTimeout(500);
|
|
404
|
+
|
|
405
|
+
const clickedSkip = await clickFirstActionable(page, SKIP_BUTTON_SELECTORS);
|
|
406
|
+
if (clickedSkip) {
|
|
407
|
+
await page.waitForTimeout(700);
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const clickedContinue = await clickFirstActionable(page, APPROVE_BUTTON_SELECTORS);
|
|
412
|
+
if (clickedContinue) {
|
|
413
|
+
await page.waitForTimeout(700);
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function selectNativeRegionOption(page) {
|
|
421
|
+
const preferred = /global|international|singapore|united states|^us$|asia|hong kong|default/i;
|
|
422
|
+
|
|
423
|
+
for (const scope of getInteractionScopes(page)) {
|
|
424
|
+
const selects = scope.locator("select");
|
|
425
|
+
const count = await selects.count().catch(() => 0);
|
|
426
|
+
for (let index = 0; index < count; index += 1) {
|
|
427
|
+
const select = selects.nth(index);
|
|
428
|
+
const visible = await select.isVisible().catch(() => false);
|
|
429
|
+
const enabled = await select.isEnabled().catch(() => true);
|
|
430
|
+
if (!visible || !enabled) continue;
|
|
431
|
+
|
|
432
|
+
const value = await select.evaluate((element, patternSource) => {
|
|
433
|
+
const matcher = new RegExp(patternSource, "i");
|
|
434
|
+
const options = [...element.options].filter((option) => !option.disabled && option.value !== "");
|
|
435
|
+
const preferredOption = options.find((option) => matcher.test(`${option.label} ${option.textContent} ${option.value}`));
|
|
436
|
+
return (preferredOption || options[0])?.value || "";
|
|
437
|
+
}, preferred.source).catch(() => "");
|
|
438
|
+
|
|
439
|
+
if (!value) continue;
|
|
440
|
+
const selected = await select.selectOption(value).then(() => true).catch(() => false);
|
|
441
|
+
if (selected) return true;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async function fillProviderOnboardingDefaults(page) {
|
|
449
|
+
let filled = false;
|
|
450
|
+
|
|
451
|
+
for (const scope of getInteractionScopes(page)) {
|
|
452
|
+
for (const { selector, value } of PROVIDER_ONBOARDING_INPUT_DEFAULTS) {
|
|
453
|
+
const locator = scope.locator(selector).first();
|
|
454
|
+
const count = await locator.count().catch(() => 0);
|
|
455
|
+
if (!count) continue;
|
|
456
|
+
|
|
457
|
+
const visible = await locator.isVisible().catch(() => false);
|
|
458
|
+
const enabled = await locator.isEnabled().catch(() => true);
|
|
459
|
+
if (!visible || !enabled) continue;
|
|
460
|
+
|
|
461
|
+
const currentValue = await locator.inputValue().catch(() => "");
|
|
462
|
+
if (currentValue) continue;
|
|
463
|
+
|
|
464
|
+
const didFill = await locator.fill(value, { timeout: 5_000 }).then(() => true).catch(() => false);
|
|
465
|
+
if (didFill) filled = true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return filled;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function clickLocatorCenter(page, locator) {
|
|
473
|
+
await locator.scrollIntoViewIfNeeded().catch(() => null);
|
|
474
|
+
const visible = await locator.isVisible().catch(() => false);
|
|
475
|
+
const enabled = await locator.isEnabled().catch(() => true);
|
|
476
|
+
if (!visible || !enabled) return false;
|
|
477
|
+
|
|
478
|
+
const box = await locator.boundingBox().catch(() => null);
|
|
479
|
+
if (!box || box.width <= 0 || box.height <= 0) return false;
|
|
480
|
+
|
|
481
|
+
const x = box.x + box.width / 2;
|
|
482
|
+
const y = box.y + box.height / 2;
|
|
483
|
+
await page.mouse.move(x, y);
|
|
484
|
+
await page.mouse.down();
|
|
485
|
+
await page.mouse.up();
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async function clickVisibleLocatorByText(page, selector, patterns) {
|
|
490
|
+
for (const scope of getInteractionScopes(page)) {
|
|
491
|
+
const locators = scope.locator(selector);
|
|
492
|
+
const count = Math.min(await locators.count().catch(() => 0), 80);
|
|
493
|
+
const candidates = [];
|
|
494
|
+
|
|
495
|
+
for (let index = 0; index < count; index += 1) {
|
|
496
|
+
const locator = locators.nth(index);
|
|
497
|
+
const visible = await locator.isVisible().catch(() => false);
|
|
498
|
+
if (!visible) continue;
|
|
499
|
+
|
|
500
|
+
const text = (await locator.innerText({ timeout: 1_000 }).catch(() => "")
|
|
501
|
+
|| await locator.textContent({ timeout: 1_000 }).catch(() => "")
|
|
502
|
+
|| "").trim();
|
|
503
|
+
if (!text) continue;
|
|
504
|
+
candidates.push({ locator, text });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
for (const pattern of patterns) {
|
|
508
|
+
const candidate = candidates.find((item) => pattern.test(item.text));
|
|
509
|
+
if (!candidate) continue;
|
|
510
|
+
const clicked = await clickLocatorCenter(page, candidate.locator).catch(() => false);
|
|
511
|
+
if (clicked) return candidate.text;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (candidates[0]) {
|
|
515
|
+
const clicked = await clickLocatorCenter(page, candidates[0].locator).catch(() => false);
|
|
516
|
+
if (clicked) return candidates[0].text;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return "";
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async function clickFirstVisibleLocatorCenter(page, selectors) {
|
|
524
|
+
for (const scope of getInteractionScopes(page)) {
|
|
525
|
+
for (const selector of selectors) {
|
|
526
|
+
const locators = scope.locator(selector);
|
|
527
|
+
const count = Math.min(await locators.count().catch(() => 0), 20);
|
|
528
|
+
for (let index = 0; index < count; index += 1) {
|
|
529
|
+
const locator = locators.nth(index);
|
|
530
|
+
const clicked = await clickLocatorCenter(page, locator).catch(() => false);
|
|
531
|
+
if (clicked) return true;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function handleCodeBuddyRegionPageWithMouse(page, reportStep) {
|
|
540
|
+
if (!isProviderPage(page) || isGoogleAuthPage(page)) return false;
|
|
541
|
+
|
|
542
|
+
const isRegionPage = await page.locator(".page-region").first().count().then(Boolean).catch(() => false);
|
|
543
|
+
if (!isRegionPage) return false;
|
|
544
|
+
|
|
545
|
+
const optionPatterns = [
|
|
546
|
+
/indonesia|^id$|\u5370\u5ea6\u5c3c\u897f\u4e9a/i,
|
|
547
|
+
/singapore|^sg$|\u65b0\u52a0\u5761/i,
|
|
548
|
+
/japan|^jp$|\u65e5\u672c/i,
|
|
549
|
+
/thailand|^th$|\u6cf0\u56fd/i,
|
|
550
|
+
/global|international|default/i,
|
|
551
|
+
];
|
|
552
|
+
|
|
553
|
+
const submitClicked = await clickFirstVisibleLocatorCenter(page, [
|
|
554
|
+
".page-region [class*='28B894']",
|
|
555
|
+
".page-region button:has-text('Get started')",
|
|
556
|
+
".page-region button:has-text('Start')",
|
|
557
|
+
".page-region button:has-text('Submit')",
|
|
558
|
+
".page-region button:has-text('Continue')",
|
|
559
|
+
".page-region [role='button']:has-text('Get started')",
|
|
560
|
+
".page-region [role='button']:has-text('Start')",
|
|
561
|
+
".page-region [role='button']:has-text('Submit')",
|
|
562
|
+
".page-region [role='button']:has-text('Continue')",
|
|
563
|
+
]);
|
|
564
|
+
if (submitClicked) {
|
|
565
|
+
reportStep("submitting_codebuddy_region", "Submitted CodeBuddy region selection");
|
|
566
|
+
await page.waitForTimeout(1200);
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const visibleOption = await clickVisibleLocatorByText(
|
|
571
|
+
page,
|
|
572
|
+
"ul.dropdown-section li, .dropdown-section li, [role='option'], .t-select-option, [class*='option']",
|
|
573
|
+
optionPatterns
|
|
574
|
+
);
|
|
575
|
+
if (visibleOption) {
|
|
576
|
+
reportStep("selecting_codebuddy_region", `Selected CodeBuddy region: ${visibleOption}`);
|
|
577
|
+
await page.waitForTimeout(900);
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const opened = await clickFirstVisibleLocatorCenter(page, [
|
|
582
|
+
".page-region .t-select",
|
|
583
|
+
".page-region [class*='t-select']",
|
|
584
|
+
".page-region [role='combobox']",
|
|
585
|
+
".page-region input[placeholder]",
|
|
586
|
+
".page-region [class*='select']",
|
|
587
|
+
".page-region [class*='cursor-pointer']",
|
|
588
|
+
]);
|
|
589
|
+
if (!opened) return false;
|
|
590
|
+
|
|
591
|
+
reportStep("opening_codebuddy_region_selector", "Opening CodeBuddy region selector");
|
|
592
|
+
await page.waitForTimeout(600);
|
|
593
|
+
|
|
594
|
+
const openedOption = await clickVisibleLocatorByText(
|
|
595
|
+
page,
|
|
596
|
+
"ul.dropdown-section li, .dropdown-section li, [role='option'], .t-select-option, [class*='option']",
|
|
597
|
+
optionPatterns
|
|
598
|
+
);
|
|
599
|
+
if (openedOption) {
|
|
600
|
+
reportStep("selecting_codebuddy_region", `Selected CodeBuddy region: ${openedOption}`);
|
|
601
|
+
await page.waitForTimeout(900);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async function handleCodeBuddyRegionPage(page, reportStep) {
|
|
608
|
+
if (!isProviderPage(page) || isGoogleAuthPage(page)) return false;
|
|
609
|
+
|
|
610
|
+
const handledWithMouse = await handleCodeBuddyRegionPageWithMouse(page, reportStep);
|
|
611
|
+
if (handledWithMouse) return true;
|
|
612
|
+
|
|
613
|
+
for (const scope of getInteractionScopes(page)) {
|
|
614
|
+
const result = await scope.evaluate(() => {
|
|
615
|
+
const visible = (element) => {
|
|
616
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
617
|
+
const style = window.getComputedStyle(element);
|
|
618
|
+
if (style.visibility === "hidden" || style.display === "none" || Number(style.opacity) === 0) return false;
|
|
619
|
+
const rect = element.getBoundingClientRect();
|
|
620
|
+
return rect.width > 0 && rect.height > 0;
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
const root = document.querySelector(".page-region");
|
|
624
|
+
const bodyText = document.body?.innerText || "";
|
|
625
|
+
const looksLikeRegionPage = root
|
|
626
|
+
|| /select\s+region|region|country|area|get started|complete/i.test(bodyText);
|
|
627
|
+
if (!looksLikeRegionPage) return null;
|
|
628
|
+
|
|
629
|
+
const clickElement = (element) => {
|
|
630
|
+
element.scrollIntoView({ block: "center", inline: "center" });
|
|
631
|
+
for (const type of ["pointerdown", "mousedown", "pointerup", "mouseup", "click"]) {
|
|
632
|
+
element.dispatchEvent(new MouseEvent(type, {
|
|
633
|
+
bubbles: true,
|
|
634
|
+
cancelable: true,
|
|
635
|
+
view: window,
|
|
636
|
+
buttons: type.endsWith("down") ? 1 : 0,
|
|
637
|
+
}));
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const optionPatterns = [
|
|
642
|
+
/indonesia|^id$|\u5370\u5ea6\u5c3c\u897f\u4e9a/i,
|
|
643
|
+
/singapore|^sg$|\u65b0\u52a0\u5761/i,
|
|
644
|
+
/japan|^jp$|\u65e5\u672c/i,
|
|
645
|
+
/thailand|^th$|\u6cf0\u56fd/i,
|
|
646
|
+
/global|international|default/i,
|
|
647
|
+
];
|
|
648
|
+
|
|
649
|
+
const searchRoot = root || document.body;
|
|
650
|
+
const submitSelectors = [
|
|
651
|
+
"button",
|
|
652
|
+
"[role='button']",
|
|
653
|
+
"input[type='submit']",
|
|
654
|
+
".t-button",
|
|
655
|
+
"[class*='button']",
|
|
656
|
+
"[class*='28B894']",
|
|
657
|
+
];
|
|
658
|
+
const submitButtons = [...searchRoot.querySelectorAll(submitSelectors.join(","))]
|
|
659
|
+
.filter(visible)
|
|
660
|
+
.filter((element) => {
|
|
661
|
+
const text = `${element.innerText || ""} ${element.getAttribute("aria-label") || ""} ${element.getAttribute("value") || ""}`;
|
|
662
|
+
const className = element.getAttribute("class") || "";
|
|
663
|
+
return /submit|start|continue|confirm|done|get started|complete|\u5b8c\u6210|\u5f00\u59cb|\u786e\u5b9a|\u4e0b\u4e00\u6b65/i.test(text)
|
|
664
|
+
|| className.includes("28B894");
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
if (submitButtons.length) {
|
|
668
|
+
clickElement(submitButtons[0]);
|
|
669
|
+
return { action: "submitted" };
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const optionSelectors = [
|
|
673
|
+
"ul.dropdown-section li",
|
|
674
|
+
".dropdown-section li",
|
|
675
|
+
"[role='option']",
|
|
676
|
+
".t-select-option",
|
|
677
|
+
"[class*='option']",
|
|
678
|
+
"[class*='dropdown'] li",
|
|
679
|
+
];
|
|
680
|
+
const options = [...document.querySelectorAll(optionSelectors.join(","))]
|
|
681
|
+
.filter(visible)
|
|
682
|
+
.filter((element) => (element.innerText || element.textContent || "").trim());
|
|
683
|
+
|
|
684
|
+
if (options.length) {
|
|
685
|
+
const option = optionPatterns
|
|
686
|
+
.map((pattern) => options.find((element) => pattern.test((element.innerText || element.textContent || "").trim())))
|
|
687
|
+
.find(Boolean) || options[0];
|
|
688
|
+
const label = (option.innerText || option.textContent || "").trim();
|
|
689
|
+
clickElement(option);
|
|
690
|
+
return { action: "selected", label };
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const controlSelectors = [
|
|
694
|
+
"[role='combobox']",
|
|
695
|
+
".t-select",
|
|
696
|
+
"[class*='t-select']",
|
|
697
|
+
"[class*='select']",
|
|
698
|
+
"input[placeholder]",
|
|
699
|
+
".text-sm",
|
|
700
|
+
"[class*='cursor-pointer']",
|
|
701
|
+
];
|
|
702
|
+
const controls = [...searchRoot.querySelectorAll(controlSelectors.join(","))]
|
|
703
|
+
.filter(visible)
|
|
704
|
+
.filter((element) => {
|
|
705
|
+
const text = `${element.innerText || ""} ${element.getAttribute("placeholder") || ""} ${element.getAttribute("aria-label") || ""}`;
|
|
706
|
+
return /region|country|area|select|placeholder|\u5730\u533a|\u56fd\u5bb6|\u9009\u62e9/i.test(text)
|
|
707
|
+
|| element.matches?.(".t-select,[class*='t-select'],input[placeholder],[class*='select']");
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
if (controls.length) {
|
|
711
|
+
clickElement(controls[0]);
|
|
712
|
+
return { action: "opened" };
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return null;
|
|
716
|
+
}).catch(() => null);
|
|
717
|
+
|
|
718
|
+
if (!result?.action) continue;
|
|
719
|
+
|
|
720
|
+
if (result.action === "selected") {
|
|
721
|
+
reportStep("selecting_codebuddy_region", `Selected CodeBuddy region${result.label ? `: ${result.label}` : ""}`);
|
|
722
|
+
await page.waitForTimeout(700);
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (result.action === "submitted") {
|
|
727
|
+
reportStep("submitting_codebuddy_region", "Submitted CodeBuddy region selection");
|
|
728
|
+
await page.waitForTimeout(1200);
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
reportStep("opening_codebuddy_region_selector", "Opening CodeBuddy region selector");
|
|
733
|
+
await page.waitForTimeout(700);
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
async function handleCodeBuddyStartedAuthorization(page, reportStep) {
|
|
741
|
+
if (!isProviderPage(page) || isGoogleAuthPage(page)) return false;
|
|
742
|
+
|
|
743
|
+
const result = await page.evaluate(async () => {
|
|
744
|
+
const url = new URL(window.location.href);
|
|
745
|
+
if (!/\/started\/?$/.test(url.pathname)) return null;
|
|
746
|
+
|
|
747
|
+
const platform = url.searchParams.get("platform") || "CLI";
|
|
748
|
+
const state = url.searchParams.get("state");
|
|
749
|
+
if (!state) return null;
|
|
750
|
+
|
|
751
|
+
const domains = [window.location.hostname || "www.codebuddy.ai"].filter(Boolean);
|
|
752
|
+
for (const domain of [...new Set(domains)]) {
|
|
753
|
+
const authUrl = new URL("/console/auth/login", window.location.origin);
|
|
754
|
+
authUrl.searchParams.set("platform", platform);
|
|
755
|
+
authUrl.searchParams.set("state", state);
|
|
756
|
+
authUrl.searchParams.set("domain", domain);
|
|
757
|
+
|
|
758
|
+
try {
|
|
759
|
+
const response = await fetch(authUrl.toString(), {
|
|
760
|
+
method: "GET",
|
|
761
|
+
credentials: "include",
|
|
762
|
+
redirect: "manual",
|
|
763
|
+
headers: {
|
|
764
|
+
"x-requested-with": "XMLHttpRequest",
|
|
765
|
+
"X-Domain": domain,
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
if (response.type === "opaqueredirect" || (response.status >= 300 && response.status < 400)) {
|
|
769
|
+
return { action: "attempted", domain, message: "redirected" };
|
|
770
|
+
}
|
|
771
|
+
const text = await response.text();
|
|
772
|
+
let data = null;
|
|
773
|
+
try {
|
|
774
|
+
data = text ? JSON.parse(text) : null;
|
|
775
|
+
} catch {
|
|
776
|
+
data = { raw: text };
|
|
777
|
+
}
|
|
778
|
+
if (response.ok && (!data || data.code === 0 || data.code === 200 || typeof data.code === "undefined")) {
|
|
779
|
+
return { action: "authorized", domain };
|
|
780
|
+
}
|
|
781
|
+
if (response.ok) {
|
|
782
|
+
return { action: "attempted", domain, code: data?.code, message: data?.msg || data?.message || "" };
|
|
783
|
+
}
|
|
784
|
+
} catch (error) {
|
|
785
|
+
// Try the next domain variant.
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return { action: "failed" };
|
|
790
|
+
}).catch(() => null);
|
|
791
|
+
|
|
792
|
+
if (!result?.action || result.action === "failed") return false;
|
|
793
|
+
|
|
794
|
+
if (result.action === "authorized") {
|
|
795
|
+
reportStep("authorizing_codebuddy_cli_state", "Authorized CodeBuddy CLI login state");
|
|
796
|
+
await page.waitForTimeout(1200);
|
|
797
|
+
return true;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
reportStep(
|
|
801
|
+
"authorizing_codebuddy_cli_state",
|
|
802
|
+
result.message
|
|
803
|
+
? `Attempted CodeBuddy CLI login state authorization: ${result.message}`
|
|
804
|
+
: "Attempted CodeBuddy CLI login state authorization"
|
|
805
|
+
);
|
|
806
|
+
await page.waitForTimeout(1200);
|
|
807
|
+
return true;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
async function handleProviderOnboarding(page, reportStep, serviceLabel) {
|
|
811
|
+
if (!isProviderPage(page) || isGoogleAuthPage(page)) return false;
|
|
812
|
+
|
|
813
|
+
const confirmedPrivacy = await clickFirstActionable(page, PRIVACY_CONFIRM_BUTTON_SELECTORS);
|
|
814
|
+
if (confirmedPrivacy) {
|
|
815
|
+
reportStep("accepting_provider_privacy_dialog", `Confirmed ${serviceLabel} privacy or terms dialog`);
|
|
816
|
+
await page.waitForTimeout(800);
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const handledCodeBuddyStarted = await handleCodeBuddyStartedAuthorization(page, reportStep);
|
|
821
|
+
if (handledCodeBuddyStarted) {
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const handledCodeBuddyRegion = await handleCodeBuddyRegionPage(page, reportStep);
|
|
826
|
+
if (handledCodeBuddyRegion) {
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const selectedNativeRegion = await selectNativeRegionOption(page);
|
|
831
|
+
if (selectedNativeRegion) {
|
|
832
|
+
reportStep("selecting_provider_region", `Selected ${serviceLabel} region`);
|
|
833
|
+
await page.waitForTimeout(700);
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const openedRegionMenu = await clickFirstActionable(page, PROVIDER_REGION_TRIGGER_SELECTORS);
|
|
838
|
+
if (openedRegionMenu) {
|
|
839
|
+
reportStep("opening_provider_region_selector", `Opening ${serviceLabel} region selector`);
|
|
840
|
+
await page.waitForTimeout(500);
|
|
841
|
+
const selectedRegion = await clickFirstActionable(page, PROVIDER_REGION_OPTION_SELECTORS);
|
|
842
|
+
if (selectedRegion) {
|
|
843
|
+
reportStep("selecting_provider_region", `Selected ${serviceLabel} region`);
|
|
844
|
+
await page.waitForTimeout(700);
|
|
845
|
+
}
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const selectedRegion = await clickFirstActionable(page, PROVIDER_REGION_OPTION_SELECTORS);
|
|
850
|
+
if (selectedRegion) {
|
|
851
|
+
reportStep("selecting_provider_region", `Selected ${serviceLabel} region`);
|
|
852
|
+
await page.waitForTimeout(700);
|
|
853
|
+
return true;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const filledDefaults = await fillProviderOnboardingDefaults(page);
|
|
857
|
+
if (filledDefaults) {
|
|
858
|
+
reportStep("filling_provider_onboarding", `Filled ${serviceLabel} onboarding defaults`);
|
|
859
|
+
await page.waitForTimeout(500);
|
|
860
|
+
return true;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const clickedAction = await clickFirstActionable(page, PROVIDER_ONBOARDING_ACTION_SELECTORS);
|
|
864
|
+
if (clickedAction) {
|
|
865
|
+
reportStep("continuing_provider_onboarding", `Continuing ${serviceLabel} onboarding`);
|
|
866
|
+
await page.waitForTimeout(1000);
|
|
867
|
+
return true;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
async function handleProviderLoginGate(page, reportStep) {
|
|
874
|
+
if (isGoogleAuthPage(page)) return false;
|
|
875
|
+
|
|
876
|
+
const confirmedExistingDialog = await clickFirstActionable(page, PRIVACY_CONFIRM_BUTTON_SELECTORS);
|
|
877
|
+
if (confirmedExistingDialog) {
|
|
878
|
+
reportStep("accepting_provider_privacy_dialog", "Confirmed provider privacy agreement dialog");
|
|
879
|
+
await page.waitForTimeout(1000);
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const checkedTerms = await checkFirstVisible(page, TERMS_CHECKBOX_SELECTORS);
|
|
884
|
+
if (checkedTerms) {
|
|
885
|
+
reportStep("accepting_provider_terms", "Accepted provider terms for Google login");
|
|
886
|
+
await page.waitForTimeout(400);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const clickedGoogle = await clickFirstActionable(page, GOOGLE_LOGIN_BUTTON_SELECTORS);
|
|
890
|
+
if (clickedGoogle) {
|
|
891
|
+
reportStep("selecting_google_login", "Selecting Google login");
|
|
892
|
+
await page.waitForTimeout(1000);
|
|
893
|
+
|
|
894
|
+
const confirmedDialog = await clickFirstActionable(page, PRIVACY_CONFIRM_BUTTON_SELECTORS);
|
|
895
|
+
if (confirmedDialog) {
|
|
896
|
+
reportStep("accepting_provider_privacy_dialog", "Confirmed provider privacy agreement dialog");
|
|
897
|
+
await page.waitForTimeout(1000);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return true;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export function createKiroCallbackMonitor(context, page, timeoutMs = DEFAULT_MANUAL_TIMEOUT_MS) {
|
|
907
|
+
return new Promise((resolve, reject) => {
|
|
908
|
+
let settled = false;
|
|
909
|
+
const trackedPages = new Set();
|
|
910
|
+
const cleanupFns = [];
|
|
911
|
+
|
|
912
|
+
const settle = (result, error = null) => {
|
|
913
|
+
if (settled) return;
|
|
914
|
+
settled = true;
|
|
915
|
+
for (const fn of cleanupFns) fn();
|
|
916
|
+
if (error) reject(error);
|
|
917
|
+
else resolve(result);
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
const registerPage = (trackedPage) => {
|
|
921
|
+
if (!trackedPage || trackedPages.has(trackedPage)) return;
|
|
922
|
+
trackedPages.add(trackedPage);
|
|
923
|
+
|
|
924
|
+
const onFrame = (frame) => {
|
|
925
|
+
const parsed = parseCallbackUrl(frame?.url?.() || "");
|
|
926
|
+
if (parsed) settle(parsed);
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
const onRequest = (request) => {
|
|
930
|
+
const parsed = parseCallbackUrl(request?.url?.() || "");
|
|
931
|
+
if (parsed) settle(parsed);
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
const onRequestFailed = (request) => {
|
|
935
|
+
const parsed = parseCallbackUrl(request?.url?.() || "");
|
|
936
|
+
if (parsed) settle(parsed);
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
const onLoadState = () => {
|
|
940
|
+
const parsed = parseCallbackUrl(trackedPage.url?.() || "");
|
|
941
|
+
if (parsed) settle(parsed);
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
trackedPage.on("framenavigated", onFrame);
|
|
945
|
+
trackedPage.on("request", onRequest);
|
|
946
|
+
trackedPage.on("requestfailed", onRequestFailed);
|
|
947
|
+
trackedPage.on("domcontentloaded", onLoadState);
|
|
948
|
+
trackedPage.on("load", onLoadState);
|
|
949
|
+
|
|
950
|
+
cleanupFns.push(() => {
|
|
951
|
+
trackedPage.off("framenavigated", onFrame);
|
|
952
|
+
trackedPage.off("request", onRequest);
|
|
953
|
+
trackedPage.off("requestfailed", onRequestFailed);
|
|
954
|
+
trackedPage.off("domcontentloaded", onLoadState);
|
|
955
|
+
trackedPage.off("load", onLoadState);
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
const current = parseCallbackUrl(trackedPage.url?.() || "");
|
|
959
|
+
if (current) settle(current);
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
const onPage = (newPage) => registerPage(newPage);
|
|
963
|
+
context.on("page", onPage);
|
|
964
|
+
cleanupFns.push(() => context.off("page", onPage));
|
|
965
|
+
|
|
966
|
+
registerPage(page);
|
|
967
|
+
|
|
968
|
+
const timeout = setTimeout(() => {
|
|
969
|
+
settle(null, new Error("Timed out waiting for Kiro callback"));
|
|
970
|
+
}, timeoutMs);
|
|
971
|
+
cleanupFns.push(() => clearTimeout(timeout));
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
export async function runGoogleAccountAutomation({
|
|
976
|
+
page,
|
|
977
|
+
authUrl,
|
|
978
|
+
email,
|
|
979
|
+
password,
|
|
980
|
+
successPromise,
|
|
981
|
+
shortTimeoutMs = DEFAULT_SHORT_TIMEOUT_MS,
|
|
982
|
+
serviceLabel = "provider",
|
|
983
|
+
openingStep = "opening_google_oauth",
|
|
984
|
+
openingMessage = "Opening Google OAuth page",
|
|
985
|
+
successStep = "oauth_success_received",
|
|
986
|
+
successMessage = "OAuth success received",
|
|
987
|
+
onStep,
|
|
988
|
+
}) {
|
|
989
|
+
const startTime = Date.now();
|
|
990
|
+
const reportStep = (step, message) => {
|
|
991
|
+
onStep?.(step, message);
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
reportStep(openingStep, openingMessage);
|
|
995
|
+
await page.goto(authUrl, { waitUntil: "domcontentloaded", timeout: 60_000 });
|
|
996
|
+
await page.waitForTimeout(2_000);
|
|
997
|
+
|
|
998
|
+
await handleProviderLoginGate(page, reportStep);
|
|
999
|
+
|
|
1000
|
+
const emailInput = await getFirstVisibleLocator(page, EMAIL_INPUT_SELECTOR);
|
|
1001
|
+
if (emailInput) {
|
|
1002
|
+
reportStep("entering_email", "Entering Google email");
|
|
1003
|
+
await emailInput.fill(email, { timeout: 15_000 });
|
|
1004
|
+
reportStep("submitting_email", "Submitting email");
|
|
1005
|
+
await clickFirstVisible(page, NEXT_BUTTON_SELECTORS);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
while (Date.now() - startTime < shortTimeoutMs) {
|
|
1009
|
+
const successResult = await Promise.race([
|
|
1010
|
+
successPromise.then((result) => ({ kind: "success", result })).catch((error) => ({ kind: "success_error", error })),
|
|
1011
|
+
new Promise((resolve) => setTimeout(() => resolve(null), 800)),
|
|
1012
|
+
]);
|
|
1013
|
+
|
|
1014
|
+
if (successResult?.kind === "success") {
|
|
1015
|
+
reportStep(successStep, successMessage);
|
|
1016
|
+
return {
|
|
1017
|
+
status: "success",
|
|
1018
|
+
...successResult.result,
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
if (successResult?.kind === "success_error") {
|
|
1023
|
+
reportStep("oauth_timeout", `Timed out waiting for ${serviceLabel} authorization`);
|
|
1024
|
+
return {
|
|
1025
|
+
status: "failed_timeout",
|
|
1026
|
+
error: successResult.error?.message || `Timed out waiting for ${serviceLabel} authorization`,
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const handledGoogleConsent = await handleGoogleConsent(page, reportStep);
|
|
1031
|
+
if (handledGoogleConsent) {
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const text = await readPageText(page);
|
|
1036
|
+
if (includesAny(text, INVALID_CREDENTIAL_MARKERS)) {
|
|
1037
|
+
reportStep("invalid_credentials", "Google rejected the supplied email or password");
|
|
1038
|
+
return {
|
|
1039
|
+
status: "failed_invalid_credentials",
|
|
1040
|
+
error: "Google rejected the supplied email or password.",
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (includesAny(text, MANUAL_ASSIST_MARKERS)) {
|
|
1045
|
+
reportStep("manual_assist_required", "Google requested CAPTCHA, 2FA, or recovery verification");
|
|
1046
|
+
return {
|
|
1047
|
+
status: "needs_manual",
|
|
1048
|
+
error: "Manual assist required in the browser session (CAPTCHA, 2FA, recovery, or suspicious-login challenge).",
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const handledOnboarding = await handleGoogleOnboarding(page, text);
|
|
1053
|
+
if (handledOnboarding) {
|
|
1054
|
+
reportStep("google_onboarding", "Accepted Google onboarding or privacy prompt");
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const handledProviderOnboarding = await handleProviderOnboarding(page, reportStep, serviceLabel);
|
|
1059
|
+
if (handledProviderOnboarding) {
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const nextEmailInput = await getFirstVisibleLocator(page, EMAIL_INPUT_SELECTOR);
|
|
1064
|
+
if (nextEmailInput) {
|
|
1065
|
+
reportStep("entering_email", "Entering Google email");
|
|
1066
|
+
await nextEmailInput.fill(email, { timeout: 15_000 });
|
|
1067
|
+
reportStep("submitting_email", "Submitting email");
|
|
1068
|
+
await clickFirstVisible(page, NEXT_BUTTON_SELECTORS);
|
|
1069
|
+
await page.waitForTimeout(700);
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const passwordInput = await getFirstVisibleLocator(page, PASSWORD_INPUT_SELECTOR);
|
|
1074
|
+
if (passwordInput) {
|
|
1075
|
+
reportStep("entering_password", "Entering Google password");
|
|
1076
|
+
await passwordInput.fill(password, { timeout: 15_000 });
|
|
1077
|
+
reportStep("submitting_password", "Submitting password");
|
|
1078
|
+
await clickFirstVisible(page, NEXT_BUTTON_SELECTORS);
|
|
1079
|
+
await page.waitForTimeout(700);
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const handledProviderGate = await handleProviderLoginGate(page, reportStep);
|
|
1084
|
+
if (handledProviderGate) {
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const clickedApprove = await clickFirstVisible(page, APPROVE_BUTTON_SELECTORS);
|
|
1089
|
+
if (clickedApprove) {
|
|
1090
|
+
reportStep("approving_consent", `Approving Google or ${serviceLabel} consent`);
|
|
1091
|
+
await page.waitForTimeout(700);
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
reportStep("waiting_for_next_screen", `Waiting for the next Google or ${serviceLabel} screen`);
|
|
1096
|
+
await page.waitForTimeout(700);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
reportStep("manual_assist_required", `Flow did not complete ${serviceLabel} authorization automatically`);
|
|
1100
|
+
return {
|
|
1101
|
+
status: "needs_manual",
|
|
1102
|
+
error: `Manual assist required in the browser session because the login flow did not complete ${serviceLabel} authorization automatically.`,
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
export async function runKiroGoogleAutomation({
|
|
1107
|
+
page,
|
|
1108
|
+
authUrl,
|
|
1109
|
+
email,
|
|
1110
|
+
password,
|
|
1111
|
+
callbackPromise,
|
|
1112
|
+
shortTimeoutMs = DEFAULT_SHORT_TIMEOUT_MS,
|
|
1113
|
+
onStep,
|
|
1114
|
+
}) {
|
|
1115
|
+
return runGoogleAccountAutomation({
|
|
1116
|
+
page,
|
|
1117
|
+
authUrl,
|
|
1118
|
+
email,
|
|
1119
|
+
password,
|
|
1120
|
+
successPromise: callbackPromise,
|
|
1121
|
+
shortTimeoutMs,
|
|
1122
|
+
serviceLabel: "Kiro",
|
|
1123
|
+
openingStep: "opening_google_oauth",
|
|
1124
|
+
openingMessage: "Opening Google OAuth page",
|
|
1125
|
+
successStep: "kiro_callback_received",
|
|
1126
|
+
successMessage: "Kiro callback received",
|
|
1127
|
+
onStep,
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
export {
|
|
1132
|
+
handleCodeBuddyRegionPage,
|
|
1133
|
+
handleProviderOnboarding,
|
|
1134
|
+
handleCodeBuddyStartedAuthorization,
|
|
1135
|
+
isProviderPage,
|
|
1136
|
+
};
|