Files
2026-05-22 01:35:42 +08:00

364 lines
14 KiB
JavaScript
Raw Permalink 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.
(function attachBackgroundStep9(root, factory) {
root.MultiPageBackgroundStep9 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep9Module() {
function createStep9Executor(deps = {}) {
const {
addLog,
chrome,
cleanupStep8NavigationListeners,
clickWithDebugger,
completeNodeFromBackground,
ensureStep8SignupPageReady,
getOAuthFlowRemainingMs,
getOAuthFlowStepTimeoutMs,
getStep8CallbackUrlFromNavigation,
getStep8CallbackUrlFromTabUpdate,
getStep8EffectLabel,
getTabId,
isTabAlive,
prepareStep8DebuggerClick,
recoverOAuthLocalhostTimeout,
reloadStep8ConsentPage,
reuseOrCreateTab,
sleepWithStop,
STEP8_CLICK_RETRY_DELAY_MS,
STEP8_MAX_ROUNDS,
STEP8_READY_WAIT_TIMEOUT_MS,
STEP8_STRATEGIES,
throwIfStep8SettledOrStopped,
triggerStep8ContentStrategy,
waitForStep8ClickEffect,
waitForStep8Ready,
setWebNavListener,
setWebNavCommittedListener,
setStep8PendingReject,
setStep8TabUpdatedListener,
shouldDeferStep9CallbackTimeout,
getStepIdByKeyForState = null,
} = deps;
const LOCALHOST_CALLBACK_LOCAL_TIMEOUT_MS = 240000;
const CALLBACK_TIMEOUT_CHECK_INTERVAL_MS = 1000;
function getVisibleStep(state, fallback = 9) {
const visibleStep = Math.floor(Number(state?.visibleStep) || 0);
return visibleStep > 0 ? visibleStep : fallback;
}
function getAuthLoginStepForVisibleStep(visibleStep) {
return visibleStep >= 12 ? Math.max(1, visibleStep - 3) : 7;
}
function getAuthLoginStepForState(state = {}, visibleStep = 9) {
const authStep = typeof getStepIdByKeyForState === 'function'
? Number(getStepIdByKeyForState('oauth-login', state))
: 0;
if (Number.isInteger(authStep) && authStep > 0) {
return authStep;
}
return getAuthLoginStepForVisibleStep(visibleStep);
}
function addStepLog(step, message, level = 'info') {
return addLog(message, level, { step, stepKey: 'confirm-oauth' });
}
async function executeStep9(state) {
const visibleStep = getVisibleStep(state, 9);
let activeState = state;
if (!activeState.oauthUrl) {
const authLoginStep = getAuthLoginStepForState(activeState, visibleStep);
throw new Error(`缺少登录用 OAuth 链接,请先完成步骤 ${authLoginStep}`);
}
await addStepLog(visibleStep, '正在监听 localhost 回调地址...');
let callbackTimeoutMs = LOCALHOST_CALLBACK_LOCAL_TIMEOUT_MS;
let timeoutRecoveryAttempted = false;
while (true) {
try {
callbackTimeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(LOCALHOST_CALLBACK_LOCAL_TIMEOUT_MS, {
step: visibleStep,
actionLabel: 'OAuth localhost 回调',
oauthUrl: activeState?.oauthUrl || '',
})
: LOCALHOST_CALLBACK_LOCAL_TIMEOUT_MS;
break;
} catch (error) {
if (timeoutRecoveryAttempted || typeof recoverOAuthLocalhostTimeout !== 'function') {
throw error;
}
const recoveredState = await recoverOAuthLocalhostTimeout({
error,
state: activeState,
visibleStep,
});
if (!recoveredState) {
throw error;
}
activeState = recoveredState;
timeoutRecoveryAttempted = true;
}
}
return new Promise((resolve, reject) => {
let resolved = false;
let signupTabId = null;
const callbackWaitStartedAt = Date.now();
let timeoutCheckTimer = null;
let timeoutDeferredLogged = false;
const cleanupListener = () => {
if (timeoutCheckTimer) {
clearTimeout(timeoutCheckTimer);
timeoutCheckTimer = null;
}
cleanupStep8NavigationListeners();
setStep8PendingReject(null);
};
const rejectStep9 = (error) => {
if (resolved) return;
resolved = true;
cleanupListener();
reject(error);
};
const finalizeStep9Callback = (callbackUrl) => {
if (resolved || !callbackUrl) return;
resolved = true;
cleanupListener();
addStepLog(visibleStep, `已捕获 localhost 地址:${callbackUrl}`, 'ok').then(() => {
return completeNodeFromBackground(state?.nodeId || 'confirm-oauth', { localhostUrl: callbackUrl });
}).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
};
const isCallbackTimeoutDeferred = async (elapsedMs) => {
if (typeof shouldDeferStep9CallbackTimeout !== 'function') {
return false;
}
try {
const deferred = await shouldDeferStep9CallbackTimeout({
tabId: signupTabId,
visibleStep,
elapsedMs,
oauthUrl: activeState?.oauthUrl || '',
});
if (deferred && !timeoutDeferredLogged) {
timeoutDeferredLogged = true;
await addStepLog(
visibleStep,
'检测到认证页仍在安全验证/授权跳转中,暂停本地回调超时判定,继续等待 localhost 回调...',
'info'
);
}
return Boolean(deferred);
} catch (error) {
await addStepLog(
visibleStep,
`复核认证页跳转状态失败(${error?.message || error}),继续按原超时规则等待回调。`,
'warn'
);
return false;
}
};
const checkCallbackTimeout = async () => {
if (resolved) {
return;
}
const elapsedMs = Date.now() - callbackWaitStartedAt;
if (await isCallbackTimeoutDeferred(elapsedMs)) {
timeoutCheckTimer = setTimeout(checkCallbackTimeout, CALLBACK_TIMEOUT_CHECK_INTERVAL_MS);
return;
}
if (elapsedMs >= LOCALHOST_CALLBACK_LOCAL_TIMEOUT_MS) {
rejectStep9(new Error(`${Math.round(LOCALHOST_CALLBACK_LOCAL_TIMEOUT_MS / 1000)} 秒内未捕获到 localhost 回调跳转,步骤 ${visibleStep} 的点击可能被拦截了。`));
return;
}
if (typeof getOAuthFlowRemainingMs === 'function') {
try {
await getOAuthFlowRemainingMs({
step: visibleStep,
actionLabel: 'OAuth localhost 回调',
oauthUrl: activeState?.oauthUrl || '',
});
} catch (error) {
rejectStep9(error);
return;
}
} else if (elapsedMs >= callbackTimeoutMs) {
rejectStep9(new Error(`${Math.round(callbackTimeoutMs / 1000)} 秒内未捕获到 localhost 回调跳转,步骤 ${visibleStep} 的点击可能被拦截了。`));
return;
}
timeoutCheckTimer = setTimeout(checkCallbackTimeout, CALLBACK_TIMEOUT_CHECK_INTERVAL_MS);
};
timeoutCheckTimer = setTimeout(
checkCallbackTimeout,
Math.min(CALLBACK_TIMEOUT_CHECK_INTERVAL_MS, Math.max(1, callbackTimeoutMs))
);
setStep8PendingReject((error) => {
rejectStep9(error);
});
setWebNavListener((details) => {
const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
finalizeStep9Callback(callbackUrl);
});
setWebNavCommittedListener((details) => {
const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
finalizeStep9Callback(callbackUrl);
});
setStep8TabUpdatedListener((tabId, changeInfo, tab) => {
const callbackUrl = getStep8CallbackUrlFromTabUpdate(tabId, changeInfo, tab, signupTabId);
finalizeStep9Callback(callbackUrl);
});
(async () => {
try {
throwIfStep8SettledOrStopped(resolved);
signupTabId = await getTabId('openai-auth');
throwIfStep8SettledOrStopped(resolved);
if (signupTabId && await isTabAlive('openai-auth')) {
await chrome.tabs.update(signupTabId, { active: true });
await addStepLog(visibleStep, '已切回认证页,正在准备调试器点击...');
} else {
signupTabId = await reuseOrCreateTab('openai-auth', activeState.oauthUrl);
await addStepLog(visibleStep, '已重新打开认证页,正在准备调试器点击...');
}
throwIfStep8SettledOrStopped(resolved);
chrome.webNavigation.onBeforeNavigate.addListener(deps.getWebNavListener());
chrome.webNavigation.onCommitted.addListener(deps.getWebNavCommittedListener());
chrome.tabs.onUpdated.addListener(deps.getStep8TabUpdatedListener());
await ensureStep8SignupPageReady(signupTabId, {
timeoutMs: typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(15000, {
step: visibleStep,
actionLabel: '等待 OAuth 同意页内容脚本就绪',
})
: 15000,
visibleStep,
logStepKey: 'confirm-oauth',
logMessage: '认证页内容脚本尚未就绪,正在等待页面恢复...',
});
for (let round = 1; round <= STEP8_MAX_ROUNDS && !resolved; round++) {
throwIfStep8SettledOrStopped(resolved);
const pageState = await waitForStep8Ready(
signupTabId,
typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(STEP8_READY_WAIT_TIMEOUT_MS, {
step: visibleStep,
actionLabel: '等待 OAuth 同意页出现',
})
: STEP8_READY_WAIT_TIMEOUT_MS,
{ visibleStep }
);
if (!pageState?.consentReady) {
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
continue;
}
const strategy = STEP8_STRATEGIES[Math.min(round - 1, STEP8_STRATEGIES.length - 1)];
await addStepLog(visibleStep, `${round}/${STEP8_MAX_ROUNDS} 轮尝试点击“继续”(${strategy.label}...`);
if (strategy.mode === 'debugger') {
const clickActionTimeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(15000, {
step: visibleStep,
actionLabel: '定位 OAuth 同意页继续按钮',
})
: 15000;
const clickTarget = await prepareStep8DebuggerClick(signupTabId, {
timeoutMs: clickActionTimeoutMs,
responseTimeoutMs: clickActionTimeoutMs,
visibleStep,
});
throwIfStep8SettledOrStopped(resolved);
await clickWithDebugger(signupTabId, clickTarget?.rect, { visibleStep });
} else {
const clickActionTimeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(15000, {
step: visibleStep,
actionLabel: '点击 OAuth 同意页继续按钮',
})
: 15000;
await triggerStep8ContentStrategy(signupTabId, strategy.strategy, {
timeoutMs: clickActionTimeoutMs,
responseTimeoutMs: clickActionTimeoutMs,
visibleStep,
});
}
if (resolved) {
return;
}
const effect = await waitForStep8ClickEffect(
signupTabId,
pageState.url,
typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(15000, {
step: visibleStep,
actionLabel: '等待 OAuth 同意页点击生效',
})
: 15000,
{ visibleStep }
);
if (resolved) {
return;
}
if (effect.progressed) {
await addStepLog(visibleStep, `检测到本次点击已生效,${getStep8EffectLabel(effect)},继续等待 localhost 回调...`, 'info');
break;
}
if (round >= STEP8_MAX_ROUNDS) {
throw new Error(`步骤 ${visibleStep}:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`);
}
await addStepLog(visibleStep, `${strategy.label} 本轮点击后页面无反应,正在刷新认证页后重试(下一轮 ${round + 1}/${STEP8_MAX_ROUNDS}...`, 'warn');
await reloadStep8ConsentPage(
signupTabId,
typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(30000, {
step: visibleStep,
actionLabel: '刷新 OAuth 同意页',
})
: 30000,
{ visibleStep }
);
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
}
} catch (err) {
rejectStep9(err);
}
})();
});
}
return { executeStep9 };
}
return { createStep9Executor };
});