console.log('[MultiPage:kiro-register-page] Content script loaded on', location.href); const KIRO_REGISTER_PAGE_LISTENER_SENTINEL = 'data-multipage-kiro-register-page-listener'; const DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS = globalThis.MultiPageKiroTimeouts?.DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS || (3 * 60 * 1000); const KIRO_CONTINUE_TEXT_PATTERN = /continue|继续/i; const KIRO_BUILDER_ID_TEXT_PATTERN = /aws\s*builder\s*id|builder\s*id/i; const KIRO_CONFIRM_CONTINUE_TEXT_PATTERN = /confirm and continue|确认并继续/i; const KIRO_ALLOW_ACCESS_TEXT_PATTERN = /allow access|允许访问/i; const KIRO_SUCCESS_TEXT_PATTERN = /authorization successful|you may now close this window|you are now signed in|授权成功|可以关闭此窗口|已登录/i; const KIRO_CLOUDFRONT_403_TEXT_PATTERN = /403 error|the request could not be satisfied|generated by cloudfront/i; const KIRO_AWS_REQUEST_ERROR_TEXT_PATTERN = /sorry,\s*there was an error processing your request\.?\s*please try again\.?|抱歉,处理您的请求时出错。请重试。?/i; const KIRO_EMAIL_TEXT_PATTERN = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi; const KIRO_NON_ACCOUNT_EMAIL_DOMAINS = new Set([ 'amazon.com', 'aws.amazon.com', 'example.com', 'kiro.dev', ]); const KIRO_EMAIL_INPUT_SELECTOR = [ 'input[placeholder="username@example.com"]', 'input[type="email"]', 'input[name="email"]', 'input[autocomplete="username"]', 'input[placeholder*="example.com" i]', ].join(', '); const KIRO_NAME_INPUT_SELECTOR = [ 'input[placeholder*="Silva" i]', 'input[autocomplete="name"]', 'input[name="name"]', 'input[name="fullName"]', ].join(', '); const KIRO_OTP_INPUT_SELECTOR = [ 'input[autocomplete="one-time-code"]', 'input[inputmode="numeric"]', 'input[placeholder*="6-digit" i]', 'input[placeholder*="6 位" i]', 'input[name*="otp" i]', 'input[name*="code" i]', ].join(', '); const KIRO_PASSWORD_FIELD_SELECTOR = 'input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"])'; const KIRO_PASSWORD_TEXT_PATTERN = /password|\u5bc6\u7801/i; const KIRO_CONFIRM_PASSWORD_TEXT_PATTERN = /confirm\s*password|re[-\s]*enter\s*password|repeat\s*password|verify\s*password|\u786e\u8ba4\s*\u5bc6\u7801|\u518d\u6b21.*\u5bc6\u7801|\u91cd\u590d.*\u5bc6\u7801/i; const KIRO_PRIMARY_PASSWORD_HINT_PATTERN = /enter\s*password|create\s*password|new\s*password|\u8f93\u5165.*\u5bc6\u7801|\u521b\u5efa.*\u5bc6\u7801|^\s*\u5bc6\u7801\s*$/i; const KIRO_SIGN_IN_TEXT_PATTERN = /sign\s*in\s*with\s*your\s*aws\s*builder\s*id|aws\s*builder\s*id\s*sign\s*in/i; const KIRO_CODE_INVALID_TEXT_PATTERN = /code\s*(?:is\s*)?invalid|invalid\s*code|\u4ee3\u7801\u65e0\u6548|\u9a8c\u8bc1\u7801\u65e0\u6548/i; const KIRO_RESEND_CODE_TEXT_PATTERN = /resend|send\s*again|\u91cd\u65b0\u53d1\u9001/i; function isVisibleKiroElement(element) { if (!element) return false; const style = window.getComputedStyle(element); const rect = element.getBoundingClientRect(); return style.display !== 'none' && style.visibility !== 'hidden' && rect.width > 0 && rect.height > 0; } function getKiroPageText() { return String(document.body?.textContent || '') .replace(/\s+/g, ' ') .trim(); } function normalizeKiroEmailCandidate(value = '') { const matched = String(value || '').match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i); return matched ? matched[0].trim().toLowerCase() : ''; } function isLikelyKiroAccountEmail(value = '') { const normalized = normalizeKiroEmailCandidate(value); if (!normalized) { return false; } const [, domain = ''] = normalized.split('@'); const normalizedDomain = domain.toLowerCase(); if (KIRO_NON_ACCOUNT_EMAIL_DOMAINS.has(normalizedDomain)) { return false; } return !normalized.startsWith('no-reply@') && !normalized.startsWith('noreply@') && !normalized.startsWith('support@') && !normalizedDomain.endsWith('.amazon.com') && !normalizedDomain.endsWith('.amazonaws.com') && !normalizedDomain.endsWith('.kiro.dev') && !normalizedDomain.endsWith('.signin.aws') && !normalizedDomain.endsWith('.profile.aws'); } function extractFirstKiroAccountEmailFromText(text = '') { const matches = String(text || '').match(KIRO_EMAIL_TEXT_PATTERN) || []; return matches.map((entry) => normalizeKiroEmailCandidate(entry)).find(isLikelyKiroAccountEmail) || ''; } function findKiroSignedInAccountEmail(pageText = '') { const selector = [ 'input[type="email"]', 'input[name*="email" i]', '[data-testid*="email" i]', '[aria-label*="email" i]', '[title*="email" i]', ].join(', '); for (const element of collectVisibleElements(selector)) { const candidate = extractFirstKiroAccountEmailFromText([ element?.value, element?.textContent, element?.getAttribute?.('aria-label'), element?.getAttribute?.('title'), ].filter(Boolean).join(' ')); if (candidate) { return candidate; } } return extractFirstKiroAccountEmailFromText(pageText); } function findKiroPageAccountEmail(pageText = '') { const selector = [ 'input[type="email"]', 'input[name*="email" i]', '[data-testid*="email" i]', '[aria-label*="email" i]', '[title*="email" i]', 'dd', 'span', 'div', 'p', ].join(', '); for (const element of collectVisibleElements(selector)) { const candidate = extractFirstKiroAccountEmailFromText([ element?.value, element?.textContent, element?.getAttribute?.('aria-label'), element?.getAttribute?.('title'), ].filter(Boolean).join(' ')); if (candidate) { return candidate; } } return extractFirstKiroAccountEmailFromText(pageText); } function collectVisibleElements(selector) { return Array.from(document.querySelectorAll(selector)) .filter((element) => isVisibleKiroElement(element)); } function findFirstVisible(selector) { return collectVisibleElements(selector)[0] || null; } function getElementActionText(element) { return [ element?.textContent, element?.value, element?.getAttribute?.('aria-label'), element?.getAttribute?.('title'), element?.getAttribute?.('data-testid'), ] .filter(Boolean) .join(' ') .replace(/\s+/g, ' ') .trim(); } function findActionButton(options = {}) { const { preferredSelectors = [], textPattern = null, formOwner = null, } = options; for (const selector of preferredSelectors) { const preferred = findFirstVisible(selector); if (preferred && !preferred.disabled && preferred.getAttribute('aria-disabled') !== 'true') { return preferred; } } const candidates = collectVisibleElements('button, [role="button"], input[type="submit"], input[type="button"]') .filter((element) => !element.disabled && element.getAttribute('aria-disabled') !== 'true'); const prioritized = formOwner ? candidates.filter((element) => (element.form || element.closest?.('form') || null) === formOwner) : []; const pool = prioritized.length ? prioritized : candidates; if (textPattern instanceof RegExp) { return pool.find((element) => textPattern.test(getElementActionText(element))) || null; } return pool[0] || null; } function findVisibleEmailInput() { return findFirstVisible(KIRO_EMAIL_INPUT_SELECTOR); } function findVisibleNameInput() { return findFirstVisible(KIRO_NAME_INPUT_SELECTOR); } function findVisibleOtpInput() { return findFirstVisible(KIRO_OTP_INPUT_SELECTOR); } function getKiroElementText(element) { return String(element?.textContent || '') .replace(/\s+/g, ' ') .trim(); } function cssEscapeKiroValue(value = '') { if (globalThis.CSS?.escape) { return globalThis.CSS.escape(String(value)); } return String(value).replace(/["\\]/g, '\\$&'); } function getKiroInputLabelText(input) { const labels = []; for (const label of Array.from(input?.labels || [])) { const text = getKiroElementText(label); if (text) labels.push(text); } const id = String(input?.id || '').trim(); if (id) { for (const label of Array.from(document.querySelectorAll(`label[for="${cssEscapeKiroValue(id)}"]`))) { const text = getKiroElementText(label); if (text) labels.push(text); } } const labelledBy = String(input?.getAttribute?.('aria-labelledby') || '').trim(); if (labelledBy) { for (const labelId of labelledBy.split(/\s+/).filter(Boolean)) { const label = document.getElementById?.(labelId); const text = getKiroElementText(label); if (text) labels.push(text); } } const parentLabel = input?.closest?.('label'); const parentLabelText = getKiroElementText(parentLabel); if (parentLabelText) labels.push(parentLabelText); return Array.from(new Set(labels)).join(' '); } function getKiroPasswordInputContext(input) { const describedBy = String(input?.getAttribute?.('aria-describedby') || '').trim(); const describedByText = describedBy ? describedBy .split(/\s+/) .map((id) => getKiroElementText(document.getElementById?.(id))) .filter(Boolean) .join(' ') : ''; return [ input?.getAttribute?.('placeholder'), input?.getAttribute?.('name'), input?.id, input?.getAttribute?.('aria-label'), input?.getAttribute?.('data-testid'), input?.getAttribute?.('autocomplete'), getKiroInputLabelText(input), describedByText, ] .filter(Boolean) .join(' ') .replace(/\s+/g, ' ') .trim(); } function createKiroPasswordCandidate(input, index) { const context = getKiroPasswordInputContext(input); const type = String(input?.getAttribute?.('type') || input?.type || '').trim().toLowerCase(); const isConfirm = KIRO_CONFIRM_PASSWORD_TEXT_PATTERN.test(context); const hasPasswordHint = KIRO_PASSWORD_TEXT_PATTERN.test(context) || type === 'password'; if (!hasPasswordHint && !isConfirm) { return null; } let primaryScore = 0; let confirmScore = 0; if (type === 'password') primaryScore += 5; if (KIRO_PASSWORD_TEXT_PATTERN.test(context)) primaryScore += 20; if (KIRO_PRIMARY_PASSWORD_HINT_PATTERN.test(context)) primaryScore += 40; if (isConfirm) { primaryScore -= 100; confirmScore += 100; } if (/^password$/i.test(String(input?.getAttribute?.('name') || ''))) primaryScore += 40; if (/^password$/i.test(String(input?.id || ''))) primaryScore += 20; return { input, index, context, isConfirm, primaryScore, confirmScore, }; } function findKiroPasswordFields() { const candidates = collectVisibleElements(KIRO_PASSWORD_FIELD_SELECTOR) .map((input, index) => createKiroPasswordCandidate(input, index)) .filter(Boolean); if (!candidates.length) { return { passwordInput: null, confirmPasswordInput: null, passwordInputs: [], }; } const confirmCandidate = candidates .filter((candidate) => candidate.isConfirm) .sort((left, right) => right.confirmScore - left.confirmScore || left.index - right.index)[0] || null; let passwordCandidate = candidates .filter((candidate) => !candidate.isConfirm) .sort((left, right) => right.primaryScore - left.primaryScore || left.index - right.index)[0] || null; if (!passwordCandidate) { passwordCandidate = candidates .filter((candidate) => !confirmCandidate || candidate.input !== confirmCandidate.input) .sort((left, right) => left.index - right.index)[0] || null; } let resolvedConfirmCandidate = confirmCandidate; if ( (!resolvedConfirmCandidate || resolvedConfirmCandidate.input === passwordCandidate?.input) && passwordCandidate ) { resolvedConfirmCandidate = candidates .filter((candidate) => candidate.input !== passwordCandidate.input) .sort((left, right) => left.index - right.index)[0] || null; } return { passwordInput: passwordCandidate?.input || null, confirmPasswordInput: resolvedConfirmCandidate?.input || null, passwordInputs: candidates.map((candidate) => candidate.input), }; } function findVisiblePasswordInputs() { return findKiroPasswordFields().passwordInputs; } function findVisibleConfirmPasswordInput() { return findKiroPasswordFields().confirmPasswordInput; } function findEmailContinueButton(emailInput = null) { const form = emailInput?.form || emailInput?.closest?.('form') || null; return findActionButton({ preferredSelectors: ['button[data-testid="test-primary-button"]'], textPattern: KIRO_CONTINUE_TEXT_PATTERN, formOwner: form, }); } function findNameContinueButton(nameInput = null) { const form = nameInput?.form || nameInput?.closest?.('form') || null; return findActionButton({ preferredSelectors: ['button[data-testid="signup-next-button"]'], textPattern: KIRO_CONTINUE_TEXT_PATTERN, formOwner: form, }); } function findOtpVerifyButton(otpInput = null) { const form = otpInput?.form || otpInput?.closest?.('form') || null; return findActionButton({ preferredSelectors: ['button[data-testid="email-verification-verify-button"]'], textPattern: KIRO_CONTINUE_TEXT_PATTERN, formOwner: form, }); } function findOtpResendButton(otpInput = null) { const form = otpInput?.form || otpInput?.closest?.('form') || null; return findActionButton({ textPattern: KIRO_RESEND_CODE_TEXT_PATTERN, formOwner: form, }); } function findPasswordContinueButton(passwordInput = null) { const form = passwordInput?.form || passwordInput?.closest?.('form') || null; return findActionButton({ preferredSelectors: ['button[data-testid="test-primary-button"]'], textPattern: KIRO_CONTINUE_TEXT_PATTERN, formOwner: form, }); } function isKiroWebHost(hostname = '') { const normalized = String(hostname || '').trim().toLowerCase(); return normalized === 'app.kiro.dev' || normalized === 'kiro.dev'; } function parseCurrentUrl() { try { return new URL(location.href); } catch (_error) { return null; } } function getKiroAuthPathKind() { const parsed = parseCurrentUrl(); const pathname = String(parsed?.pathname || '').toLowerCase(); if (/(^|\/)login(\/|$)/.test(pathname)) { return 'login'; } if (/(^|\/)signup(\/|$)/.test(pathname)) { return 'signup'; } return ''; } function findBuilderIdButton() { return findActionButton({ textPattern: KIRO_BUILDER_ID_TEXT_PATTERN, }); } function isKiroWebSignedInPage(pageText = '') { const parsedUrl = parseCurrentUrl(); const pathname = String(parsedUrl?.pathname || '').replace(/\/+$/, '') || '/'; const authStatus = String(parsedUrl?.searchParams?.get('auth_status') || '').trim().toLowerCase(); if (authStatus === 'success') { return true; } if (pathname !== '/signin') { return true; } return /signed\s*in|authorization\s*successful|登录成功|已登录|授权成功/i.test(pageText); } function getAuthorizationActionKind(text = '') { if (KIRO_CONFIRM_CONTINUE_TEXT_PATTERN.test(text)) { return 'confirm_continue'; } if (KIRO_ALLOW_ACCESS_TEXT_PATTERN.test(text)) { return 'allow_access'; } return 'unknown'; } function findAuthorizationActionButton() { return findActionButton({ preferredSelectors: [ 'button[data-testid="confirm-button"]', 'button[data-testid="allow-access-button"]', ], textPattern: /confirm and continue|allow access|确认并继续|允许访问/i, }); } function isKiroAwsAuthHost(hostname = '') { const normalized = String(hostname || '').trim().toLowerCase(); return normalized === 'view.awsapps.com' || normalized === 'login.awsapps.com' || normalized === 'profile.aws.amazon.com' || normalized === 'profile.aws' || normalized.endsWith('.profile.aws.amazon.com') || normalized.endsWith('.profile.aws') || normalized === 'signin.aws.amazon.com' || normalized === 'signin.aws' || normalized.endsWith('.signin.aws.amazon.com') || normalized.endsWith('.signin.aws'); } function detectKiroFatalPageState(pageText = '', currentUrl = '', pageTitle = '') { const combinedText = `${pageTitle} ${pageText}`.replace(/\s+/g, ' ').trim(); const normalizedHost = String(location.hostname || '').trim().toLowerCase(); const isKiroAuthHost = isKiroAwsAuthHost(normalizedHost); const matchedSignals = [ /403 error/i.test(combinedText), /the request could not be satisfied/i.test(combinedText), /generated by cloudfront/i.test(combinedText), ].filter(Boolean).length; if (isKiroAuthHost && KIRO_AWS_REQUEST_ERROR_TEXT_PATTERN.test(combinedText)) { return { state: 'proxy_error_page', url: currentUrl, fatalMessage: 'Kiro 注册页出现 AWS 请求异常,通常是当前代理 IP 或出口区域异常,请先切换代理后再重试。', }; } if (matchedSignals >= 2 || (isKiroAuthHost && matchedSignals >= 1 && KIRO_CLOUDFRONT_403_TEXT_PATTERN.test(combinedText))) { return { state: 'cloudfront_403_page', url: currentUrl, fatalMessage: 'Kiro 注册页返回 403(CloudFront 拒绝请求),通常是当前代理 IP 或区域触发了 AWS 风控,请更换代理后重试。', }; } return null; } function isKiroFatalState(state = '') { return state === 'cloudfront_403_page' || state === 'proxy_error_page'; } function getKiroFatalStateMessage(snapshot = {}) { if (snapshot?.fatalMessage) { return snapshot.fatalMessage; } switch (snapshot?.state) { case 'cloudfront_403_page': return 'Kiro 注册页返回 403(CloudFront 拒绝请求),通常是当前代理 IP 或区域触发了 AWS 风控,请更换代理后重试。'; case 'proxy_error_page': return 'Kiro 注册页出现 AWS 请求异常,通常是当前代理 IP 或出口区域异常,请先切换代理后再重试。'; default: return `Kiro 页面出现异常状态:${snapshot?.state || 'unknown'}`; } } function detectKiroRegisterPageState() { const pageText = getKiroPageText(); const currentUrl = location.href; const authPathKind = getKiroAuthPathKind(); const pageEmail = findKiroPageAccountEmail(pageText); const fatalState = detectKiroFatalPageState(pageText, currentUrl, document.title || ''); if (fatalState) { return fatalState; } if (isKiroWebHost(location.hostname || '')) { const builderIdButton = findBuilderIdButton(); if (builderIdButton) { return { state: 'kiro_signin_page', url: currentUrl, email: pageEmail, accountEmail: pageEmail, actionButton: builderIdButton, actionText: getElementActionText(builderIdButton), }; } if (isKiroWebSignedInPage(pageText)) { const accountEmail = findKiroSignedInAccountEmail(pageText); return { state: 'kiro_web_signed_in', url: currentUrl, accountEmail, email: accountEmail, }; } } const passwordFields = findKiroPasswordFields(); if (passwordFields.passwordInput) { const passwordInput = passwordFields.passwordInput; const isLoginPasswordPage = authPathKind === 'login' || (!passwordFields.confirmPasswordInput && KIRO_SIGN_IN_TEXT_PATTERN.test(pageText)); return { state: isLoginPasswordPage ? 'login_password_page' : 'create_password_page', url: currentUrl, email: pageEmail, accountEmail: pageEmail, passwordInput, confirmPasswordInput: passwordFields.confirmPasswordInput, continueButton: findPasswordContinueButton(passwordInput), }; } const authorizationButton = findAuthorizationActionButton(); if (authorizationButton) { const authorizationActionText = getElementActionText(authorizationButton); return { state: 'authorization_page', url: currentUrl, email: pageEmail, accountEmail: pageEmail, actionButton: authorizationButton, authorizationActionKind: getAuthorizationActionKind(authorizationActionText), authorizationActionText, }; } if ( KIRO_SUCCESS_TEXT_PATTERN.test(pageText) || /request approved|access your data|请求已批准|访问你的数据/i.test(pageText) ) { return { state: 'success_page', url: currentUrl, email: pageEmail, accountEmail: pageEmail, }; } const otpInput = findVisibleOtpInput(); if (otpInput) { return { state: authPathKind === 'login' ? 'login_otp_page' : 'register_otp_page', url: currentUrl, email: pageEmail, accountEmail: pageEmail, otpInput, verifyButton: findOtpVerifyButton(otpInput), resendButton: findOtpResendButton(otpInput), codeInvalid: KIRO_CODE_INVALID_TEXT_PATTERN.test(pageText), }; } const nameInput = findVisibleNameInput(); if (nameInput) { return { state: 'name_entry', url: currentUrl, email: pageEmail, accountEmail: pageEmail, nameInput, continueButton: findNameContinueButton(nameInput), }; } const emailInput = findVisibleEmailInput(); if (emailInput) { return { state: 'email_entry', url: currentUrl, email: pageEmail, accountEmail: pageEmail, emailInput, continueButton: findEmailContinueButton(emailInput), }; } return { state: 'loading', url: currentUrl, email: pageEmail, accountEmail: pageEmail, }; } async function waitForKiroState(predicate, options = {}) { const timeoutMs = Math.max(1000, Math.floor(Number(options.timeoutMs) || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS)); const retryDelayMs = Math.max(100, Math.floor(Number(options.retryDelayMs) || 250)); const start = Date.now(); while (Date.now() - start < timeoutMs) { throwIfStopped(); const detected = detectKiroRegisterPageState(); if (isKiroFatalState(detected.state)) { throw new Error(getKiroFatalStateMessage(detected)); } if (predicate(detected)) { return detected; } await sleep(retryDelayMs); } const finalState = detectKiroRegisterPageState(); if (isKiroFatalState(finalState.state)) { throw new Error(getKiroFatalStateMessage(finalState)); } throw new Error(options.timeoutMessage || `等待 Kiro 页面状态超时:${finalState.state}`); } async function ensureKiroRegisterPageState(payload = {}) { const targetStates = Array.isArray(payload?.targetStates) ? payload.targetStates.map((entry) => String(entry || '').trim()).filter(Boolean) : []; if (!targetStates.length) { throw new Error('缺少 Kiro 目标页面状态。'); } return waitForKiroState( (detected) => targetStates.includes(detected.state), { timeoutMs: payload?.timeoutMs, retryDelayMs: payload?.retryDelayMs, timeoutMessage: payload?.timeoutMessage || `等待 Kiro 页面进入 ${targetStates.join(' / ')} 超时,当前页面:${location.href}`, } ); } async function waitForKiroRegisterStateChange(payload = {}) { const fromStates = Array.isArray(payload?.fromStates) ? payload.fromStates.map((entry) => String(entry || '').trim()).filter(Boolean) : []; if (!fromStates.length) { throw new Error('缺少 Kiro 原始页面状态。'); } return waitForKiroState( (detected) => { if (detected.state === 'loading') { return false; } if (payload?.returnOnCodeInvalid && detected.state === 'register_otp_page' && detected.codeInvalid) { return true; } return !fromStates.includes(detected.state); }, { timeoutMs: payload?.timeoutMs, retryDelayMs: payload?.retryDelayMs, timeoutMessage: payload?.timeoutMessage || `等待 Kiro 页面离开 ${fromStates.join(' / ')} 超时,当前页面:${location.href}`, } ); } async function waitForKiroAuthorizationAdvance(previousState = {}, options = {}) { return waitForKiroState( (detected) => { if (detected.state === 'success_page' || detected.state === 'kiro_web_signed_in') { return true; } if (detected.state !== 'authorization_page') { return false; } const previousKind = String(previousState?.authorizationActionKind || '').trim(); const previousText = String(previousState?.authorizationActionText || '').trim(); const previousUrl = String(previousState?.url || '').trim(); const nextKind = String(detected.authorizationActionKind || '').trim(); const nextText = String(detected.authorizationActionText || '').trim(); return nextKind !== previousKind || nextText !== previousText || String(detected.url || '').trim() !== previousUrl; }, { timeoutMs: options.timeoutMs, retryDelayMs: options.retryDelayMs, timeoutMessage: options.timeoutMessage || `等待 Kiro 授权页进入下一步超时:${location.href}`, } ); } async function selectKiroBuilderId() { const readyState = await ensureKiroRegisterPageState({ targetStates: ['kiro_signin_page'], timeoutMs: DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, retryDelayMs: 250, }); if (!readyState.actionButton) { throw new Error('Kiro 官方登录页未找到 Builder ID 登录按钮。'); } simulateClick(readyState.actionButton); return { submitted: true, state: 'builder_id_selected', url: location.href, actionText: readyState.actionText || '', }; } async function submitKiroEmail(payload = {}) { const email = String(payload?.email || '').trim(); if (!email) { throw new Error('缺少 Kiro 注册邮箱,无法继续提交。'); } const readyState = await ensureKiroRegisterPageState({ targetStates: ['email_entry'], timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, retryDelayMs: payload?.retryDelayMs || 250, }); if (!readyState.emailInput || !readyState.continueButton) { throw new Error('Kiro 邮箱页未找到可用的输入框或继续按钮。'); } fillInput(readyState.emailInput, email); await sleep(200); simulateClick(readyState.continueButton); return { submitted: true, state: 'email_submitted', url: location.href, }; } async function submitKiroName(payload = {}) { const fullName = String(payload?.fullName || '').trim(); if (!fullName) { throw new Error('缺少 Kiro 注册姓名,无法继续提交。'); } const readyState = await ensureKiroRegisterPageState({ targetStates: ['name_entry'], timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, retryDelayMs: payload?.retryDelayMs || 250, }); if (!readyState.nameInput || !readyState.continueButton) { throw new Error('Kiro 姓名页未找到可用的输入框或继续按钮。'); } fillInput(readyState.nameInput, fullName); await sleep(200); simulateClick(readyState.continueButton); return { submitted: true, state: 'name_submitted', url: location.href, }; } async function submitKiroVerificationCode(payload = {}) { const code = String(payload?.code || '').trim(); if (!code) { throw new Error('缺少 Kiro 邮箱验证码,无法继续提交。'); } const readyState = await ensureKiroRegisterPageState({ targetStates: ['register_otp_page'], timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, retryDelayMs: payload?.retryDelayMs || 250, }); if (!readyState.otpInput || !readyState.verifyButton) { throw new Error('Kiro 验证码页未找到可用的输入框或继续按钮。'); } fillInput(readyState.otpInput, code); await sleep(200); simulateClick(readyState.verifyButton); return { submitted: true, state: 'verification_submitted', url: location.href, }; } async function submitKiroPassword(payload = {}) { const password = String(payload?.password || ''); if (!password) { throw new Error('缺少 Kiro 账户密码,无法继续提交。'); } const readyState = await ensureKiroRegisterPageState({ targetStates: ['create_password_page'], timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, retryDelayMs: payload?.retryDelayMs || 250, }); if (!readyState.passwordInput || !readyState.continueButton) { throw new Error('Kiro 密码页未找到可用的密码框或继续按钮。'); } fillInput(readyState.passwordInput, password); await sleep(150); if (String(readyState.passwordInput.value || '') !== password) { throw new Error('Kiro \u5bc6\u7801\u9875\u4e3b\u5bc6\u7801\u6846\u586b\u5165\u5931\u8d25\uff0c\u5df2\u505c\u6b62\u63d0\u4ea4\uff0c\u8bf7\u68c0\u67e5\u9875\u9762\u5b57\u6bb5\u7ed3\u6784\u3002'); } const confirmPasswordInput = readyState.confirmPasswordInput || (() => { const passwordInputs = findVisiblePasswordInputs(); return passwordInputs.find((input) => input !== readyState.passwordInput) || null; })(); if (confirmPasswordInput && confirmPasswordInput !== readyState.passwordInput) { fillInput(confirmPasswordInput, password); await sleep(150); if (String(confirmPasswordInput.value || '') !== password) { throw new Error('Kiro \u5bc6\u7801\u9875\u786e\u8ba4\u5bc6\u7801\u6846\u586b\u5165\u5931\u8d25\uff0c\u5df2\u505c\u6b62\u63d0\u4ea4\uff0c\u8bf7\u68c0\u67e5\u9875\u9762\u5b57\u6bb5\u7ed3\u6784\u3002'); } } if ( confirmPasswordInput && confirmPasswordInput !== readyState.passwordInput && String(confirmPasswordInput.value || '') !== String(readyState.passwordInput.value || '') ) { throw new Error('Kiro \u5bc6\u7801\u9875\u4e24\u4e2a\u5bc6\u7801\u6846\u5185\u5bb9\u4e0d\u4e00\u81f4\uff0c\u5df2\u505c\u6b62\u63d0\u4ea4\u3002'); } simulateClick(readyState.continueButton); return { submitted: true, state: 'password_submitted', url: location.href, }; } async function confirmKiroRegisterConsent(payload = {}) { let currentState = await ensureKiroRegisterPageState({ targetStates: ['authorization_page', 'success_page', 'kiro_web_signed_in'], timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, retryDelayMs: payload?.retryDelayMs || 250, }); const maxActions = Math.max(1, Math.min(4, Number(payload?.maxActions) || 3)); const actions = []; while (currentState.state === 'authorization_page' && actions.length < maxActions) { if (!currentState.actionButton) { throw new Error('Kiro 授权页未找到可用的授权按钮。'); } actions.push({ kind: currentState.authorizationActionKind || 'unknown', text: currentState.authorizationActionText || getElementActionText(currentState.actionButton), }); simulateClick(currentState.actionButton); currentState = await waitForKiroAuthorizationAdvance(currentState, { timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, retryDelayMs: payload?.retryDelayMs || 250, timeoutMessage: 'Kiro 授权按钮点击后页面未继续,请检查当前授权页状态。', }); } if (currentState.state !== 'success_page' && currentState.state !== 'kiro_web_signed_in') { throw new Error('Kiro 授权页未完成确认访问流程。'); } return { submitted: true, state: currentState.state, url: currentState.url || location.href, actions, }; } async function handleKiroRegisterCommand(message) { switch (message.type) { case 'GET_KIRO_REGISTER_PAGE_STATE': { const detected = detectKiroRegisterPageState(); return { state: detected?.state || '', url: detected?.url || location.href, accountEmail: detected?.accountEmail || '', email: detected?.email || detected?.accountEmail || '', fatalMessage: detected?.fatalMessage || '', authorizationActionKind: detected?.authorizationActionKind || '', authorizationActionText: detected?.authorizationActionText || '', actionText: detected?.actionText || '', codeInvalid: Boolean(detected?.codeInvalid), }; } case 'ENSURE_KIRO_PAGE_STATE': return ensureKiroRegisterPageState(message.payload || {}); case 'ENSURE_KIRO_STATE_CHANGE': return waitForKiroRegisterStateChange(message.payload || {}); case 'EXECUTE_NODE': { const nodeId = String(message.nodeId || message.payload?.nodeId || '').trim(); if (nodeId === 'kiro-open-register-page') { return selectKiroBuilderId(); } if (nodeId === 'kiro-submit-email') { return submitKiroEmail(message.payload || {}); } if (nodeId === 'kiro-submit-name') { return submitKiroName(message.payload || {}); } if (nodeId === 'kiro-submit-verification-code') { return submitKiroVerificationCode(message.payload || {}); } if (nodeId === 'kiro-submit-password') { return submitKiroPassword(message.payload || {}); } if (nodeId === 'kiro-complete-register-consent') { return confirmKiroRegisterConsent(message.payload || {}); } throw new Error(`register-page.js 不处理节点:${nodeId}`); } default: return null; } } if (document.documentElement.getAttribute(KIRO_REGISTER_PAGE_LISTENER_SENTINEL) !== '1') { document.documentElement.setAttribute(KIRO_REGISTER_PAGE_LISTENER_SENTINEL, '1'); chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { if ( message.type === 'ENSURE_KIRO_PAGE_STATE' || message.type === 'ENSURE_KIRO_STATE_CHANGE' || message.type === 'GET_KIRO_REGISTER_PAGE_STATE' || message.type === 'EXECUTE_NODE' ) { resetStopState(); handleKiroRegisterCommand(message) .then((result) => { sendResponse({ ok: true, ...(result || {}) }); }) .catch((error) => { if (isStopError(error)) { sendResponse({ stopped: true, error: error.message }); return; } sendResponse({ error: error?.message || String(error || '未知错误') }); }); return true; } return false; }); }