fix: stop kiro auto-run on proxy error pages
This commit is contained in:
@@ -8847,6 +8847,14 @@ function isSignupUserAlreadyExistsFailure(error) {
|
|||||||
return /SIGNUP_USER_ALREADY_EXISTS::|user_already_exists/i.test(message);
|
return /SIGNUP_USER_ALREADY_EXISTS::|user_already_exists/i.test(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isKiroProxyFailure(error) {
|
||||||
|
if (typeof loggingStatus !== 'undefined' && loggingStatus?.isKiroProxyFailure) {
|
||||||
|
return loggingStatus.isKiroProxyFailure(error);
|
||||||
|
}
|
||||||
|
const message = getErrorMessage(error);
|
||||||
|
return /Kiro\s*(?:注册页|桌面授权页).*(?:CloudFront\s*拒绝请求|AWS\s*请求异常)|(?:当前代理\s*IP|出口区域异常).*(?:切换代理|更换代理)|AWS\s*风控.*(?:切换代理|更换代理)/i.test(message);
|
||||||
|
}
|
||||||
|
|
||||||
function isStep4Route405RecoveryLimitFailure(error) {
|
function isStep4Route405RecoveryLimitFailure(error) {
|
||||||
const message = getErrorMessage(error);
|
const message = getErrorMessage(error);
|
||||||
return /STEP4_405_RECOVERY_LIMIT::|步骤\s*4:检测到\s*405\s*错误页面,已连续点击“重试”恢复/i.test(message);
|
return /STEP4_405_RECOVERY_LIMIT::|步骤\s*4:检测到\s*405\s*错误页面,已连续点击“重试”恢复/i.test(message);
|
||||||
@@ -11752,6 +11760,7 @@ const autoRunController = self.MultiPageBackgroundAutoRunController?.createAutoR
|
|||||||
isPhoneSmsPlatformRateLimitFailure,
|
isPhoneSmsPlatformRateLimitFailure,
|
||||||
isPlusCheckoutNonFreeTrialFailure,
|
isPlusCheckoutNonFreeTrialFailure,
|
||||||
isGpcTaskEndedFailure,
|
isGpcTaskEndedFailure,
|
||||||
|
isKiroProxyFailure,
|
||||||
isRestartCurrentAttemptError,
|
isRestartCurrentAttemptError,
|
||||||
isStep4Route405RecoveryLimitFailure,
|
isStep4Route405RecoveryLimitFailure,
|
||||||
isSignupUserAlreadyExistsFailure,
|
isSignupUserAlreadyExistsFailure,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
hasSavedNodeProgress,
|
hasSavedNodeProgress,
|
||||||
isAddPhoneAuthFailure,
|
isAddPhoneAuthFailure,
|
||||||
isGpcTaskEndedFailure,
|
isGpcTaskEndedFailure,
|
||||||
|
isKiroProxyFailure,
|
||||||
isPhoneSmsPlatformRateLimitFailure,
|
isPhoneSmsPlatformRateLimitFailure,
|
||||||
isPlusCheckoutNonFreeTrialFailure,
|
isPlusCheckoutNonFreeTrialFailure,
|
||||||
isRestartCurrentAttemptError,
|
isRestartCurrentAttemptError,
|
||||||
@@ -704,11 +705,15 @@
|
|||||||
&& isSignupUserAlreadyExistsFailure(err);
|
&& isSignupUserAlreadyExistsFailure(err);
|
||||||
const blockedByStep4Route405 = typeof isStep4Route405RecoveryLimitFailure === 'function'
|
const blockedByStep4Route405 = typeof isStep4Route405RecoveryLimitFailure === 'function'
|
||||||
&& isStep4Route405RecoveryLimitFailure(err);
|
&& isStep4Route405RecoveryLimitFailure(err);
|
||||||
|
const blockedByKiroProxy = typeof isKiroProxyFailure === 'function'
|
||||||
|
&& isKiroProxyFailure(err);
|
||||||
const canRetry = !blockedByAddPhone
|
const canRetry = !blockedByAddPhone
|
||||||
&& !blockedByPhoneNoSupply
|
&& !blockedByPhoneNoSupply
|
||||||
&& !blockedByPlusNonFreeTrial
|
&& !blockedByPlusNonFreeTrial
|
||||||
&& !blockedByGpcTaskEnded
|
&& !blockedByGpcTaskEnded
|
||||||
&& !blockedBySignupUserAlreadyExists
|
&& !blockedBySignupUserAlreadyExists
|
||||||
|
&& !blockedByStep4Route405
|
||||||
|
&& !blockedByKiroProxy
|
||||||
&& autoRunSkipFailures
|
&& autoRunSkipFailures
|
||||||
&& attemptRun < maxAttemptsForRound;
|
&& attemptRun < maxAttemptsForRound;
|
||||||
|
|
||||||
@@ -926,6 +931,27 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blockedByKiroProxy) {
|
||||||
|
roundSummary.status = 'failed';
|
||||||
|
roundSummary.finalFailureReason = reason;
|
||||||
|
await setState({
|
||||||
|
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
|
||||||
|
});
|
||||||
|
await appendRoundRecordIfNeeded('failed', reason, err);
|
||||||
|
cancelPendingCommands('当前轮检测到 Kiro 代理异常页,已停止自动运行,等待用户切换代理。');
|
||||||
|
await broadcastStopToContentScripts();
|
||||||
|
await addLog(`第 ${targetRun}/${totalRuns} 轮检测到 Kiro 代理异常页:${reason}`, 'error');
|
||||||
|
await addLog('当前代理可能不可用,请先切换代理后再继续。自动运行已停止。', 'warn');
|
||||||
|
stoppedEarly = true;
|
||||||
|
await broadcastAutoRunStatus('stopped', {
|
||||||
|
currentRun: targetRun,
|
||||||
|
totalRuns,
|
||||||
|
attemptRun,
|
||||||
|
sessionId: 0,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (canRetry) {
|
if (canRetry) {
|
||||||
const retryIndex = attemptRun;
|
const retryIndex = attemptRun;
|
||||||
if (isRestartCurrentAttemptError(err)) {
|
if (isRestartCurrentAttemptError(err)) {
|
||||||
|
|||||||
@@ -143,6 +143,11 @@
|
|||||||
return /SIGNUP_USER_ALREADY_EXISTS::|user_already_exists/i.test(message);
|
return /SIGNUP_USER_ALREADY_EXISTS::|user_already_exists/i.test(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isKiroProxyFailure(error) {
|
||||||
|
const message = getErrorMessage(error);
|
||||||
|
return /Kiro\s*(?:注册页|桌面授权页).*(?:CloudFront\s*拒绝请求|AWS\s*请求异常)|(?:当前代理\s*IP|出口区域异常).*(?:切换代理|更换代理)|AWS\s*风控.*(?:切换代理|更换代理)/i.test(message);
|
||||||
|
}
|
||||||
|
|
||||||
function isStep9RecoverableAuthError(error) {
|
function isStep9RecoverableAuthError(error) {
|
||||||
const message = String(typeof error === 'string' ? error : error?.message || '');
|
const message = String(typeof error === 'string' ? error : error?.message || '');
|
||||||
return /STEP9_OAUTH_RETRY::/i.test(message)
|
return /STEP9_OAUTH_RETRY::/i.test(message)
|
||||||
@@ -236,6 +241,7 @@
|
|||||||
getSourceLabel,
|
getSourceLabel,
|
||||||
hasSavedNodeProgress,
|
hasSavedNodeProgress,
|
||||||
hasSavedProgress,
|
hasSavedProgress,
|
||||||
|
isKiroProxyFailure,
|
||||||
isLegacyStep9RecoverableAuthError,
|
isLegacyStep9RecoverableAuthError,
|
||||||
isRestartCurrentAttemptError,
|
isRestartCurrentAttemptError,
|
||||||
isSignupUserAlreadyExistsFailure,
|
isSignupUserAlreadyExistsFailure,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const KIRO_DESKTOP_AUTHORIZE_LISTENER_SENTINEL = 'data-multipage-kiro-desktop-au
|
|||||||
const KIRO_DESKTOP_ALLOW_TEXT_PATTERN = /allow access|authorize|continue|allow|允许访问|授权|继续/i;
|
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_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_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 = [
|
const KIRO_DESKTOP_EMAIL_INPUT_SELECTOR = [
|
||||||
'input[placeholder="username@example.com"]',
|
'input[placeholder="username@example.com"]',
|
||||||
@@ -94,8 +95,30 @@ function findDesktopActionButton(options = {}) {
|
|||||||
return pool[0] || 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 = '') {
|
function detectDesktopFatalState(pageText = '', currentUrl = '', pageTitle = '') {
|
||||||
const combinedText = `${pageTitle} ${pageText}`.replace(/\s+/g, ' ').trim();
|
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)) {
|
if (KIRO_DESKTOP_CLOUDFRONT_403_TEXT_PATTERN.test(combinedText)) {
|
||||||
return {
|
return {
|
||||||
state: 'cloudfront_403_page',
|
state: 'cloudfront_403_page',
|
||||||
@@ -204,8 +227,8 @@ function detectKiroDesktopAuthorizeState() {
|
|||||||
|
|
||||||
async function getCurrentDesktopAuthorizeState() {
|
async function getCurrentDesktopAuthorizeState() {
|
||||||
const detected = detectKiroDesktopAuthorizeState();
|
const detected = detectKiroDesktopAuthorizeState();
|
||||||
if (detected.state === 'cloudfront_403_page') {
|
if (detected.state === 'cloudfront_403_page' || detected.state === 'proxy_error_page') {
|
||||||
throw new Error(detected.fatalMessage || 'Kiro 桌面授权页出现 403。');
|
throw new Error(detected.fatalMessage || 'Kiro 桌面授权页出现代理异常。');
|
||||||
}
|
}
|
||||||
if (detected.state === 'callback_error') {
|
if (detected.state === 'callback_error') {
|
||||||
throw new Error(`Kiro 桌面授权回调失败:${detected.error || 'unknown_error'}`);
|
throw new Error(`Kiro 桌面授权回调失败:${detected.error || 'unknown_error'}`);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const KIRO_CONFIRM_CONTINUE_TEXT_PATTERN = /confirm and continue|确认并继续
|
|||||||
const KIRO_ALLOW_ACCESS_TEXT_PATTERN = /allow access|允许访问/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_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_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_INPUT_SELECTOR = [
|
const KIRO_EMAIL_INPUT_SELECTOR = [
|
||||||
'input[placeholder="username@example.com"]',
|
'input[placeholder="username@example.com"]',
|
||||||
@@ -193,20 +194,39 @@ function findAuthorizationActionButton() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = '') {
|
function detectKiroFatalPageState(pageText = '', currentUrl = '', pageTitle = '') {
|
||||||
const combinedText = `${pageTitle} ${pageText}`.replace(/\s+/g, ' ').trim();
|
const combinedText = `${pageTitle} ${pageText}`.replace(/\s+/g, ' ').trim();
|
||||||
const normalizedHost = String(location.hostname || '').trim().toLowerCase();
|
const normalizedHost = String(location.hostname || '').trim().toLowerCase();
|
||||||
const isAwsProfileHost = normalizedHost === 'profile.aws.amazon.com'
|
const isKiroAuthHost = isKiroAwsAuthHost(normalizedHost);
|
||||||
|| normalizedHost === 'profile.aws'
|
|
||||||
|| normalizedHost.endsWith('.profile.aws.amazon.com')
|
|
||||||
|| normalizedHost.endsWith('.profile.aws');
|
|
||||||
const matchedSignals = [
|
const matchedSignals = [
|
||||||
/403 error/i.test(combinedText),
|
/403 error/i.test(combinedText),
|
||||||
/the request could not be satisfied/i.test(combinedText),
|
/the request could not be satisfied/i.test(combinedText),
|
||||||
/generated by cloudfront/i.test(combinedText),
|
/generated by cloudfront/i.test(combinedText),
|
||||||
].filter(Boolean).length;
|
].filter(Boolean).length;
|
||||||
|
|
||||||
if (matchedSignals >= 2 || (isAwsProfileHost && matchedSignals >= 1 && KIRO_CLOUDFRONT_403_TEXT_PATTERN.test(combinedText))) {
|
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 {
|
return {
|
||||||
state: 'cloudfront_403_page',
|
state: 'cloudfront_403_page',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
@@ -218,7 +238,8 @@ function detectKiroFatalPageState(pageText = '', currentUrl = '', pageTitle = ''
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isKiroFatalState(state = '') {
|
function isKiroFatalState(state = '') {
|
||||||
return state === 'cloudfront_403_page';
|
return state === 'cloudfront_403_page'
|
||||||
|
|| state === 'proxy_error_page';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKiroFatalStateMessage(snapshot = {}) {
|
function getKiroFatalStateMessage(snapshot = {}) {
|
||||||
@@ -228,6 +249,8 @@ function getKiroFatalStateMessage(snapshot = {}) {
|
|||||||
switch (snapshot?.state) {
|
switch (snapshot?.state) {
|
||||||
case 'cloudfront_403_page':
|
case 'cloudfront_403_page':
|
||||||
return 'Kiro 注册页返回 403(CloudFront 拒绝请求),通常是当前代理 IP 或区域触发了 AWS 风控,请更换代理后重试。';
|
return 'Kiro 注册页返回 403(CloudFront 拒绝请求),通常是当前代理 IP 或区域触发了 AWS 风控,请更换代理后重试。';
|
||||||
|
case 'proxy_error_page':
|
||||||
|
return 'Kiro 注册页出现 AWS 请求异常,通常是当前代理 IP 或出口区域异常,请先切换代理后再重试。';
|
||||||
default:
|
default:
|
||||||
return `Kiro 页面出现异常状态:${snapshot?.state || 'unknown'}`;
|
return `Kiro 页面出现异常状态:${snapshot?.state || 'unknown'}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,3 +244,177 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
|
|||||||
assert.deepStrictEqual(executedNodeIds, ['kiro-open-register-page']);
|
assert.deepStrictEqual(executedNodeIds, ['kiro-open-register-page']);
|
||||||
assert.equal(helperCalls, 1);
|
assert.equal(helperCalls, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('auto-run controller stops immediately on kiro proxy failures even when skip-failures is enabled', async () => {
|
||||||
|
const source = fs.readFileSync('background/auto-run-controller.js', 'utf8');
|
||||||
|
const globalScope = {};
|
||||||
|
const controllerApi = new Function('self', `${source}; return self.MultiPageBackgroundAutoRunController;`)(globalScope);
|
||||||
|
|
||||||
|
const kiroNodeIds = [
|
||||||
|
'kiro-open-register-page',
|
||||||
|
'kiro-submit-email',
|
||||||
|
'kiro-submit-name',
|
||||||
|
'kiro-submit-verification-code',
|
||||||
|
'kiro-submit-password',
|
||||||
|
'kiro-complete-register-consent',
|
||||||
|
'kiro-start-desktop-authorize',
|
||||||
|
'kiro-complete-desktop-authorize',
|
||||||
|
'kiro-upload-credential',
|
||||||
|
];
|
||||||
|
const events = {
|
||||||
|
logs: [],
|
||||||
|
phases: [],
|
||||||
|
runCalls: 0,
|
||||||
|
cancelReasons: [],
|
||||||
|
records: [],
|
||||||
|
};
|
||||||
|
let currentState = {
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
flowId: 'kiro',
|
||||||
|
autoRunSkipFailures: true,
|
||||||
|
autoRunFallbackThreadIntervalMinutes: 0,
|
||||||
|
autoRunDelayEnabled: false,
|
||||||
|
autoRunDelayMinutes: 30,
|
||||||
|
autoStepDelaySeconds: null,
|
||||||
|
nodeStatuses: {
|
||||||
|
'kiro-open-register-page': 'pending',
|
||||||
|
'kiro-submit-email': 'pending',
|
||||||
|
'kiro-submit-name': 'pending',
|
||||||
|
'kiro-submit-verification-code': 'pending',
|
||||||
|
'kiro-submit-password': 'pending',
|
||||||
|
'kiro-complete-register-consent': 'pending',
|
||||||
|
'kiro-start-desktop-authorize': 'pending',
|
||||||
|
'kiro-complete-desktop-authorize': 'pending',
|
||||||
|
'kiro-upload-credential': 'pending',
|
||||||
|
},
|
||||||
|
tabRegistry: {},
|
||||||
|
sourceLastUrls: {},
|
||||||
|
autoRunRoundSummaries: [],
|
||||||
|
};
|
||||||
|
let sessionSeed = 900;
|
||||||
|
const runtime = {
|
||||||
|
state: {
|
||||||
|
autoRunActive: false,
|
||||||
|
autoRunCurrentRun: 0,
|
||||||
|
autoRunTotalRuns: 1,
|
||||||
|
autoRunAttemptRun: 0,
|
||||||
|
autoRunSessionId: 0,
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return { ...this.state };
|
||||||
|
},
|
||||||
|
set(updates = {}) {
|
||||||
|
this.state = { ...this.state, ...updates };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const controller = controllerApi.createAutoRunController({
|
||||||
|
addLog: async (message, level = 'info') => {
|
||||||
|
events.logs.push({ message, level });
|
||||||
|
},
|
||||||
|
appendAccountRunRecord: async (status, _state, reason) => {
|
||||||
|
events.records.push({ status, reason });
|
||||||
|
return { status, reason };
|
||||||
|
},
|
||||||
|
AUTO_RUN_MAX_RETRIES_PER_ROUND: 3,
|
||||||
|
AUTO_RUN_RETRY_DELAY_MS: 3000,
|
||||||
|
AUTO_RUN_TIMER_KIND_BEFORE_RETRY: 'before_retry',
|
||||||
|
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS: 'between_rounds',
|
||||||
|
broadcastAutoRunStatus: async (phase, payload = {}) => {
|
||||||
|
events.phases.push({ phase, ...payload });
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
|
||||||
|
autoRunPhase: phase,
|
||||||
|
autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0,
|
||||||
|
autoRunTotalRuns: payload.totalRuns ?? currentState.autoRunTotalRuns ?? 1,
|
||||||
|
autoRunAttemptRun: payload.attemptRun ?? currentState.autoRunAttemptRun ?? 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
broadcastStopToContentScripts: async () => {},
|
||||||
|
cancelPendingCommands: (reason) => {
|
||||||
|
events.cancelReasons.push(reason);
|
||||||
|
},
|
||||||
|
clearStopRequest: () => {},
|
||||||
|
createAutoRunSessionId: () => {
|
||||||
|
sessionSeed += 1;
|
||||||
|
return sessionSeed;
|
||||||
|
},
|
||||||
|
ensureHotmailMailboxReadyForAutoRunRound: async () => {},
|
||||||
|
getAutoRunStatusPayload: (phase, payload = {}) => ({
|
||||||
|
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
|
||||||
|
autoRunPhase: phase,
|
||||||
|
autoRunCurrentRun: payload.currentRun ?? 0,
|
||||||
|
autoRunTotalRuns: payload.totalRuns ?? 1,
|
||||||
|
autoRunAttemptRun: payload.attemptRun ?? 0,
|
||||||
|
autoRunSessionId: payload.sessionId ?? 0,
|
||||||
|
}),
|
||||||
|
getErrorMessage: (error) => error?.message || String(error || ''),
|
||||||
|
getFirstUnfinishedNodeId: (statuses = {}) => {
|
||||||
|
for (const nodeId of kiroNodeIds) {
|
||||||
|
const status = String(statuses?.[nodeId] || 'pending').trim().toLowerCase();
|
||||||
|
if (!['completed', 'manual_completed', 'skipped'].includes(status)) {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
getPendingAutoRunTimerPlan: () => null,
|
||||||
|
getRunningNodeIds: () => [],
|
||||||
|
getState: async () => ({
|
||||||
|
...currentState,
|
||||||
|
nodeStatuses: { ...(currentState.nodeStatuses || {}) },
|
||||||
|
tabRegistry: { ...(currentState.tabRegistry || {}) },
|
||||||
|
sourceLastUrls: { ...(currentState.sourceLastUrls || {}) },
|
||||||
|
}),
|
||||||
|
getStopRequested: () => false,
|
||||||
|
hasSavedNodeProgress: () => false,
|
||||||
|
isAddPhoneAuthFailure: () => false,
|
||||||
|
isGpcTaskEndedFailure: () => false,
|
||||||
|
isKiroProxyFailure: (error) => /Kiro 注册页出现 AWS 请求异常|Kiro 注册页返回 403|切换代理|更换代理/i.test(error?.message || String(error || '')),
|
||||||
|
isPhoneSmsPlatformRateLimitFailure: () => false,
|
||||||
|
isPlusCheckoutNonFreeTrialFailure: () => false,
|
||||||
|
isRestartCurrentAttemptError: () => false,
|
||||||
|
isStep4Route405RecoveryLimitFailure: () => false,
|
||||||
|
isSignupUserAlreadyExistsFailure: () => false,
|
||||||
|
isStopError: () => false,
|
||||||
|
launchAutoRunTimerPlan: async () => false,
|
||||||
|
normalizeAutoRunFallbackThreadIntervalMinutes: (value) => Math.max(0, Number(value) || 0),
|
||||||
|
persistAutoRunTimerPlan: async () => {},
|
||||||
|
resetState: async () => {},
|
||||||
|
runAutoSequenceFromNode: async (nodeId) => {
|
||||||
|
events.runCalls += 1;
|
||||||
|
assert.equal(nodeId, 'kiro-open-register-page');
|
||||||
|
throw new Error('Kiro 注册页出现 AWS 请求异常,通常是当前代理 IP 或出口区域异常,请先切换代理后再重试。');
|
||||||
|
},
|
||||||
|
runtime,
|
||||||
|
setState: async (updates = {}) => {
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
...updates,
|
||||||
|
nodeStatuses: updates.nodeStatuses ? { ...updates.nodeStatuses } : currentState.nodeStatuses,
|
||||||
|
tabRegistry: updates.tabRegistry ? { ...updates.tabRegistry } : currentState.tabRegistry,
|
||||||
|
sourceLastUrls: updates.sourceLastUrls ? { ...updates.sourceLastUrls } : currentState.sourceLastUrls,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
sleepWithStop: async () => {},
|
||||||
|
throwIfAutoRunSessionStopped: () => {},
|
||||||
|
waitForRunningNodesToFinish: async () => currentState,
|
||||||
|
chrome: {
|
||||||
|
runtime: {
|
||||||
|
sendMessage: () => Promise.resolve(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await controller.autoRunLoop(2, { autoRunSkipFailures: true, mode: 'restart' });
|
||||||
|
|
||||||
|
assert.equal(events.runCalls, 1);
|
||||||
|
assert.equal(events.phases[0]?.phase, 'running');
|
||||||
|
assert.equal(events.phases.at(-1)?.phase, 'stopped');
|
||||||
|
assert.equal(events.phases.some((entry) => entry.phase === 'retrying'), false);
|
||||||
|
assert.equal(events.cancelReasons.length, 1);
|
||||||
|
assert.match(events.cancelReasons[0], /Kiro 代理异常页/);
|
||||||
|
assert.ok(events.logs.some((entry) => /切换代理/.test(entry.message)));
|
||||||
|
assert.ok(events.records.some((entry) => /failed$/i.test(entry.status)));
|
||||||
|
});
|
||||||
|
|||||||
@@ -50,4 +50,16 @@ test('logging/status add-phone detection ignores step 2 phone-entry switch failu
|
|||||||
loggingStatus.getErrorMessage(new Error('GPC_TASK_ENDED::GPC OTP 超时,请重新创建任务')),
|
loggingStatus.getErrorMessage(new Error('GPC_TASK_ENDED::GPC OTP 超时,请重新创建任务')),
|
||||||
'GPC OTP 超时,请重新创建任务'
|
'GPC OTP 超时,请重新创建任务'
|
||||||
);
|
);
|
||||||
|
assert.equal(
|
||||||
|
loggingStatus.isKiroProxyFailure('Kiro 注册页出现 AWS 请求异常,通常是当前代理 IP 或出口区域异常,请先切换代理后再重试。'),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
loggingStatus.isKiroProxyFailure('Kiro 注册页返回 403(CloudFront 拒绝请求),通常是当前代理 IP 或区域触发了 AWS 风控,请更换代理后重试。'),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
loggingStatus.isKiroProxyFailure('步骤 2:邮箱已提交,当前已进入姓名页。'),
|
||||||
|
false
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const vm = require('node:vm');
|
||||||
|
|
||||||
|
const source = fs.readFileSync('content/kiro/register-page.js', 'utf8');
|
||||||
|
|
||||||
|
function createHarness({ href, hostname, title = '', bodyText = '' }) {
|
||||||
|
const context = {
|
||||||
|
console: { log() {}, warn() {}, error() {}, info() {} },
|
||||||
|
location: { href, hostname },
|
||||||
|
document: {
|
||||||
|
title,
|
||||||
|
body: {
|
||||||
|
textContent: bodyText,
|
||||||
|
},
|
||||||
|
documentElement: {
|
||||||
|
getAttribute() {
|
||||||
|
return '1';
|
||||||
|
},
|
||||||
|
setAttribute() {},
|
||||||
|
},
|
||||||
|
querySelector() {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
querySelectorAll() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
window: {},
|
||||||
|
globalThis: null,
|
||||||
|
resetStopState() {},
|
||||||
|
isStopError() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
throwIfStopped() {},
|
||||||
|
sleep() {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
fillInput() {},
|
||||||
|
MouseEvent: class {},
|
||||||
|
PointerEvent: class {},
|
||||||
|
KeyboardEvent: class {},
|
||||||
|
};
|
||||||
|
context.window = context;
|
||||||
|
context.globalThis = context;
|
||||||
|
context.window.getComputedStyle = () => ({ display: 'block', visibility: 'visible' });
|
||||||
|
|
||||||
|
vm.createContext(context);
|
||||||
|
vm.runInContext(source, context);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('kiro register content detects aws request error page as a proxy failure', () => {
|
||||||
|
const harness = createHarness({
|
||||||
|
href: 'https://profile.aws.amazon.com/signup',
|
||||||
|
hostname: 'profile.aws.amazon.com',
|
||||||
|
title: '错误',
|
||||||
|
bodyText: '抱歉,处理您的请求时出错。请重试。',
|
||||||
|
});
|
||||||
|
|
||||||
|
const detected = harness.detectKiroRegisterPageState();
|
||||||
|
|
||||||
|
assert.equal(detected.state, 'proxy_error_page');
|
||||||
|
assert.match(detected.fatalMessage, /切换代理/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('kiro register content does not misclassify the normal name page as a proxy failure', () => {
|
||||||
|
const harness = createHarness({
|
||||||
|
href: 'https://profile.aws.amazon.com/signup',
|
||||||
|
hostname: 'profile.aws.amazon.com',
|
||||||
|
title: 'Enter your name',
|
||||||
|
bodyText: '输入您的姓名 继续',
|
||||||
|
});
|
||||||
|
|
||||||
|
const fatal = harness.detectKiroFatalPageState('输入您的姓名 继续', harness.location.href, harness.document.title);
|
||||||
|
|
||||||
|
assert.equal(fatal, null);
|
||||||
|
});
|
||||||
@@ -41,7 +41,7 @@ test('sidepanel persists and locks SUB2API account priority setting', () => {
|
|||||||
/inputSub2ApiAccountPriority\.value = String\(normalizeSub2ApiAccountPriorityValue\(state\?\.sub2apiAccountPriority\)\);/
|
/inputSub2ApiAccountPriority\.value = String\(normalizeSub2ApiAccountPriorityValue\(state\?\.sub2apiAccountPriority\)\);/
|
||||||
);
|
);
|
||||||
assert.match(source, /applyFlowSettingsGroupVisibility\(visibleGroupIds\);/);
|
assert.match(source, /applyFlowSettingsGroupVisibility\(visibleGroupIds\);/);
|
||||||
assert.match(flowRegistrySource, /'openai-source-sub2api': \{[\s\S]*'row-sub2api-account-priority'/);
|
assert.match(flowRegistrySource, /'openai-target-sub2api': \{[\s\S]*'row-sub2api-account-priority'/);
|
||||||
assert.match(source, /inputSub2ApiAccountPriority\.disabled = locked;/);
|
assert.match(source, /inputSub2ApiAccountPriority\.disabled = locked;/);
|
||||||
assert.match(
|
assert.match(
|
||||||
source,
|
source,
|
||||||
|
|||||||
Reference in New Issue
Block a user