Files
FlowPilot/content/kiro/desktop-authorize-page.js
T
2026-05-19 02:04:32 +08:00

359 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
console.log('[MultiPage:kiro-desktop-authorize-page] Content script loaded on', location.href);
const KIRO_DESKTOP_AUTHORIZE_LISTENER_SENTINEL = 'data-multipage-kiro-desktop-authorize-page-listener';
const KIRO_DESKTOP_ALLOW_TEXT_PATTERN = /allow access|authorize|continue|allow|允许访问|授权|继续/i;
const KIRO_DESKTOP_LOADING_TEXT_PATTERN = /loading|redirecting|please wait|加载中|跳转中|请稍候/i;
const KIRO_DESKTOP_CLOUDFRONT_403_TEXT_PATTERN = /403 error|the request could not be satisfied|generated by cloudfront/i;
const KIRO_DESKTOP_AWS_REQUEST_ERROR_TEXT_PATTERN = /sorry,\s*there was an error processing your request\.?\s*please try again\.?|抱歉,处理您的请求时出错。请重试。?/i;
const KIRO_DESKTOP_EMAIL_INPUT_SELECTOR = [
'input[placeholder="username@example.com"]',
'input[type="email"]',
'input[name="email"]',
'input[autocomplete="username"]',
].join(', ');
const KIRO_DESKTOP_PASSWORD_INPUT_SELECTOR = [
'input[type="password"]',
'input[name="password"]',
'input[id*="password" i]',
'input[autocomplete="current-password"]',
].join(', ');
const KIRO_DESKTOP_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(', ');
function isVisibleDesktopElement(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 collectVisibleDesktopElements(selector) {
return Array.from(document.querySelectorAll(selector))
.filter((element) => isVisibleDesktopElement(element));
}
function findFirstVisibleDesktopElement(selector) {
return collectVisibleDesktopElements(selector)[0] || null;
}
function getDesktopPageText() {
return String(document.body?.textContent || '')
.replace(/\s+/g, ' ')
.trim();
}
function getDesktopActionText(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 findDesktopActionButton(options = {}) {
const {
preferredSelectors = [],
textPattern = null,
formOwner = null,
} = options;
for (const selector of preferredSelectors) {
const preferred = findFirstVisibleDesktopElement(selector);
if (preferred && !preferred.disabled && preferred.getAttribute('aria-disabled') !== 'true') {
return preferred;
}
}
const candidates = collectVisibleDesktopElements('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(getDesktopActionText(element))) || null;
}
return pool[0] || null;
}
function isKiroDesktopAuthHost(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 detectDesktopFatalState(pageText = '', currentUrl = '', pageTitle = '') {
const combinedText = `${pageTitle} ${pageText}`.replace(/\s+/g, ' ').trim();
const isKiroAuthHost = isKiroDesktopAuthHost(location.hostname || '');
if (isKiroAuthHost && KIRO_DESKTOP_AWS_REQUEST_ERROR_TEXT_PATTERN.test(combinedText)) {
return {
state: 'proxy_error_page',
url: currentUrl,
fatalMessage: 'Kiro 桌面授权页出现 AWS 请求异常,通常是当前代理 IP 或出口区域异常,请先切换代理后再重试。',
};
}
if (KIRO_DESKTOP_CLOUDFRONT_403_TEXT_PATTERN.test(combinedText)) {
return {
state: 'cloudfront_403_page',
url: currentUrl,
fatalMessage: 'Kiro 桌面授权页返回 403CloudFront 拒绝请求),通常是当前代理 IP 或区域触发了 AWS 风控,请更换代理后重试。',
};
}
return null;
}
function detectKiroDesktopAuthorizeState() {
const currentUrl = location.href;
const pageText = getDesktopPageText();
const fatalState = detectDesktopFatalState(pageText, currentUrl, document.title || '');
if (fatalState) {
return fatalState;
}
if (/^https?:\/\/(127\.0\.0\.1|localhost):/i.test(currentUrl)) {
const parsed = new URL(currentUrl);
if (parsed.searchParams.get('error')) {
return {
state: 'callback_error',
url: currentUrl,
error: parsed.searchParams.get('error_description') || parsed.searchParams.get('error') || 'unknown_error',
};
}
return {
state: 'callback_page',
url: currentUrl,
code: parsed.searchParams.get('code') || '',
stateValue: parsed.searchParams.get('state') || '',
};
}
const otpInput = findFirstVisibleDesktopElement(KIRO_DESKTOP_OTP_INPUT_SELECTOR);
if (otpInput) {
return {
state: 'otp_page',
url: currentUrl,
otpInput,
continueButton: findDesktopActionButton({
preferredSelectors: ['button[data-testid="email-verification-verify-button"]'],
textPattern: KIRO_DESKTOP_ALLOW_TEXT_PATTERN,
formOwner: otpInput.form || otpInput.closest?.('form') || null,
}),
};
}
const emailInput = findFirstVisibleDesktopElement(KIRO_DESKTOP_EMAIL_INPUT_SELECTOR);
if (emailInput) {
return {
state: 'relogin_email',
url: currentUrl,
emailInput,
continueButton: findDesktopActionButton({
preferredSelectors: ['button[data-testid="test-primary-button"]'],
textPattern: KIRO_DESKTOP_ALLOW_TEXT_PATTERN,
formOwner: emailInput.form || emailInput.closest?.('form') || null,
}),
};
}
const passwordInput = findFirstVisibleDesktopElement(KIRO_DESKTOP_PASSWORD_INPUT_SELECTOR);
if (passwordInput) {
return {
state: 'relogin_password',
url: currentUrl,
passwordInput,
continueButton: findDesktopActionButton({
preferredSelectors: ['button[data-testid="test-primary-button"]'],
textPattern: KIRO_DESKTOP_ALLOW_TEXT_PATTERN,
formOwner: passwordInput.form || passwordInput.closest?.('form') || null,
}),
};
}
const consentButton = findDesktopActionButton({
preferredSelectors: [
'button[data-testid="confirm-button"]',
'button[data-testid="allow-access-button"]',
],
textPattern: KIRO_DESKTOP_ALLOW_TEXT_PATTERN,
});
if (consentButton) {
return {
state: 'consent_page',
url: currentUrl,
actionButton: consentButton,
actionText: getDesktopActionText(consentButton),
};
}
if (KIRO_DESKTOP_LOADING_TEXT_PATTERN.test(pageText)) {
return {
state: 'redirecting',
url: currentUrl,
};
}
return {
state: 'loading',
url: currentUrl,
};
}
async function getCurrentDesktopAuthorizeState() {
const detected = detectKiroDesktopAuthorizeState();
if (detected.state === 'cloudfront_403_page' || detected.state === 'proxy_error_page') {
throw new Error(detected.fatalMessage || 'Kiro 桌面授权页出现代理异常。');
}
if (detected.state === 'callback_error') {
throw new Error(`Kiro 桌面授权回调失败:${detected.error || 'unknown_error'}`);
}
return detected;
}
async function submitDesktopEmail(payload = {}) {
const email = String(payload?.email || '').trim();
if (!email) {
throw new Error('缺少桌面授权邮箱,无法继续。');
}
const currentState = await getCurrentDesktopAuthorizeState();
if (currentState.state !== 'relogin_email' || !currentState.emailInput || !currentState.continueButton) {
throw new Error(`当前桌面授权页不是邮箱重登状态:${currentState.state}`);
}
fillInput(currentState.emailInput, email);
await sleep(150);
simulateClick(currentState.continueButton);
return {
submitted: true,
state: 'email_submitted',
url: location.href,
};
}
async function submitDesktopPassword(payload = {}) {
const password = String(payload?.password || '');
if (!password) {
throw new Error('缺少桌面授权密码,无法继续。');
}
const currentState = await getCurrentDesktopAuthorizeState();
if (currentState.state !== 'relogin_password' || !currentState.passwordInput || !currentState.continueButton) {
throw new Error(`当前桌面授权页不是密码重登状态:${currentState.state}`);
}
fillInput(currentState.passwordInput, password);
await sleep(150);
simulateClick(currentState.continueButton);
return {
submitted: true,
state: 'password_submitted',
url: location.href,
};
}
async function submitDesktopOtp(payload = {}) {
const code = String(payload?.code || '').trim();
if (!code) {
throw new Error('缺少桌面授权验证码,无法继续。');
}
const currentState = await getCurrentDesktopAuthorizeState();
if (currentState.state !== 'otp_page' || !currentState.otpInput || !currentState.continueButton) {
throw new Error(`当前桌面授权页不是验证码状态:${currentState.state}`);
}
fillInput(currentState.otpInput, code);
await sleep(150);
simulateClick(currentState.continueButton);
return {
submitted: true,
state: 'otp_submitted',
url: location.href,
};
}
async function confirmDesktopConsent() {
const currentState = await getCurrentDesktopAuthorizeState();
if (currentState.state !== 'consent_page' || !currentState.actionButton) {
throw new Error(`当前桌面授权页不是授权确认状态:${currentState.state}`);
}
simulateClick(currentState.actionButton);
return {
submitted: true,
state: 'consent_submitted',
url: location.href,
actionText: currentState.actionText || '',
};
}
async function handleKiroDesktopAuthorizeCommand(message) {
switch (message.type) {
case 'GET_KIRO_DESKTOP_AUTHORIZE_STATE':
return getCurrentDesktopAuthorizeState();
case 'EXECUTE_KIRO_DESKTOP_AUTHORIZE_ACTION': {
const action = String(message.payload?.action || '').trim();
if (action === 'submit-email') {
return submitDesktopEmail(message.payload || {});
}
if (action === 'submit-password') {
return submitDesktopPassword(message.payload || {});
}
if (action === 'submit-otp') {
return submitDesktopOtp(message.payload || {});
}
if (action === 'confirm-consent') {
return confirmDesktopConsent();
}
throw new Error(`desktop-authorize-page.js 不处理动作:${action}`);
}
default:
return null;
}
}
if (document.documentElement.getAttribute(KIRO_DESKTOP_AUTHORIZE_LISTENER_SENTINEL) !== '1') {
document.documentElement.setAttribute(KIRO_DESKTOP_AUTHORIZE_LISTENER_SENTINEL, '1');
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
if (
message.type === 'GET_KIRO_DESKTOP_AUTHORIZE_STATE'
|| message.type === 'EXECUTE_KIRO_DESKTOP_AUTHORIZE_ACTION'
) {
resetStopState();
handleKiroDesktopAuthorizeCommand(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;
});
}