优化步骤在日志中的显示

This commit is contained in:
QLHazyCoder
2026-05-03 19:25:55 +08:00
parent 4410fea3d1
commit 691ac39514
25 changed files with 691 additions and 297 deletions
+174 -76
View File
@@ -443,6 +443,18 @@ function getStepDefinitionForState(step, state = {}) {
return getStepDefinitionsForState(state).find((definition) => Number(definition.id) === numericStep) || null;
}
function getStepIdByKeyForState(stepKey, state = {}) {
const normalizedKey = String(stepKey || '').trim();
if (!normalizedKey) return null;
const ids = getStepIdsForState(state);
for (const id of ids) {
if (String(getStepDefinitionForState(id, state)?.key || '').trim() === normalizedKey) {
return Number(id);
}
}
return null;
}
initializeSessionStorageAccess();
setupDeclarativeNetRequestRules();
@@ -6395,13 +6407,20 @@ async function sendToMailContentScriptResilient(mail, message, options = {}) {
// Logging
// ============================================================
async function addLog(message, level = 'info') {
async function addLog(message, level = 'info', options = {}) {
if (typeof loggingStatus !== 'undefined' && loggingStatus?.addLog) {
return loggingStatus.addLog(message, level);
return loggingStatus.addLog(message, level, options);
}
const state = await getState();
const logs = state.logs || [];
const entry = { message, level, timestamp: Date.now() };
const step = Math.floor(Number(options?.step) || 0);
const entry = {
message: String(message || ''),
level,
timestamp: Date.now(),
step: step > 0 ? step : null,
stepKey: String(options?.stepKey || '').trim(),
};
logs.push(entry);
if (logs.length > 500) logs.splice(0, logs.length - 500);
await setState({ logs });
@@ -7484,13 +7503,14 @@ async function humanStepDelay(min = HUMAN_STEP_DELAY_MIN, max = HUMAN_STEP_DELAY
await sleepWithStop(duration);
}
async function clickWithDebugger(tabId, rect) {
async function clickWithDebugger(tabId, rect, options = {}) {
const visibleStep = Math.floor(Number(options.visibleStep) || 0) || 9;
throwIfStopped();
if (!tabId) {
throw new Error('未找到用于调试点击的认证页面标签页。');
}
if (!rect || !Number.isFinite(rect.centerX) || !Number.isFinite(rect.centerY)) {
throw new Error('步骤 9 的调试器兜底点击需要有效的按钮坐标。');
throw new Error(`步骤 ${visibleStep} 的调试器兜底点击需要有效的按钮坐标。`);
}
const target = { tabId };
@@ -7498,7 +7518,7 @@ async function clickWithDebugger(tabId, rect) {
await chrome.debugger.attach(target, '1.3');
} catch (err) {
throw new Error(
`步骤 9 的调试器兜底点击附加失败:${err.message}` +
`步骤 ${visibleStep} 的调试器兜底点击附加失败:${err.message}` +
'如果认证页标签已打开 DevTools,请先关闭后重试。'
);
}
@@ -7843,7 +7863,7 @@ async function runCompletedStepSideEffects(step, payload, completionState, lastS
async function reportCompletedStepSideEffectError(step, error) {
const message = getErrorMessage(error);
console.warn(LOG_PREFIX, `[completeStepFromBackground] step ${step} post-completion side effect failed:`, error);
await addLog(`步骤 ${step} 已完成,但完成后的收尾处理失败:${message}`, 'warn');
await addLog(`已完成,但完成后的收尾处理失败:${message}`, 'warn', { step });
}
async function completeStepFromBackground(step, payload = {}) {
@@ -7860,7 +7880,7 @@ async function completeStepFromBackground(step, payload = {}) {
: (typeof LAST_STEP_ID === 'number' ? LAST_STEP_ID : 10);
const completionState = step === lastStepId ? latestState : null;
await setStepStatus(step, 'completed');
await addLog(`步骤 ${step} 已完成`, 'ok');
await addLog('已完成', 'ok', { step });
if (step === lastStepId) {
notifyStepComplete(step, payload);
@@ -7891,13 +7911,13 @@ async function finalizeDeferredStepExecutionError(step, error) {
if (isStopError(error)) {
await setStepStatus(step, 'stopped');
await addLog(`步骤 ${step} 已被用户停止`, 'warn');
await addLog('已被用户停止', 'warn', { step });
await appendManualAccountRunRecordIfNeeded(`step${step}_stopped`, latestState, getErrorMessage(error));
return;
}
await setStepStatus(step, 'failed');
await addLog(`步骤 ${step} 失败:${getErrorMessage(error)}`, 'error');
await addLog(`失败:${getErrorMessage(error)}`, 'error', { step });
await appendManualAccountRunRecordIfNeeded(`step${step}_failed`, latestState, getErrorMessage(error));
}
@@ -8159,7 +8179,7 @@ async function executeStep(step, options = {}) {
throwIfStopped();
try {
await setStepStatus(step, 'running');
await addLog(`步骤 ${step} 开始执行`);
await addLog('开始执行', 'info', { step });
await humanStepDelay();
const fetchRetryPolicy = typeof getStepFetchNetworkRetryPolicy === 'function'
? getStepFetchNetworkRetryPolicy(step)
@@ -8222,7 +8242,7 @@ async function executeStep(step, options = {}) {
const errorState = await getState();
if (isStopError(err)) {
await setStepStatus(step, 'stopped');
await addLog(`步骤 ${step} 已被用户停止`, 'warn');
await addLog('已被用户停止', 'warn', { step });
await appendManualAccountRunRecordIfNeeded(`step${step}_stopped`, errorState, getErrorMessage(err));
throw err;
}
@@ -8236,7 +8256,7 @@ async function executeStep(step, options = {}) {
}
if (!(deferRetryableTransportError && doesStepUseCompletionSignal(step, errorState) && isRetryableContentScriptTransportError(err))) {
await setStepStatus(step, 'failed');
await addLog(`步骤 ${step} 失败:${err.message}`, 'error');
await addLog(`失败:${err.message}`, 'error', { step });
await appendManualAccountRunRecordIfNeeded(`step${step}_failed`, errorState, getErrorMessage(err));
} else {
console.warn(
@@ -10209,7 +10229,10 @@ async function refreshOAuthUrlBeforeStep6(state, options = {}) {
throw new Error(`步骤 ${visibleStep}:当前自动流程预期使用贡献模式,但运行态 contributionMode 已丢失,已阻止回退到普通 CPA / SUB2API / Codex2API 链路。请重新进入贡献模式后再点击自动。`);
}
if (state?.contributionMode && contributionOAuthManager?.startContributionFlow) {
await addLog(`步骤 ${visibleStep}contributionMode=true,走公开贡献接口,正在申请 OAuth 登录地址...`, 'info');
await addLog('contributionMode=true,走公开贡献接口,正在申请 OAuth 登录地址...', 'info', {
step: visibleStep,
stepKey: 'oauth-login',
});
const contributionState = await contributionOAuthManager.startContributionFlow({
nickname: state.contributionNickname || '',
openAuthTab: false,
@@ -10222,7 +10245,10 @@ async function refreshOAuthUrlBeforeStep6(state, options = {}) {
await handleStepData(1, { oauthUrl });
return oauthUrl;
}
await addLog(`步骤 ${visibleStep}contributionMode=false,走普通 CPA / SUB2API / Codex2API 链路(当前面板:${getPanelModeLabel(state)}),正在刷新 OAuth 登录地址...`, 'info');
await addLog(`contributionMode=false,走普通 CPA / SUB2API / Codex2API 链路(当前面板:${getPanelModeLabel(state)}),正在刷新 OAuth 登录地址...`, 'info', {
step: visibleStep,
stepKey: 'oauth-login',
});
console.log(LOG_PREFIX, '[refreshOAuthUrlBeforeStep6] requesting fresh OAuth directly from panel');
const refreshResult = await requestOAuthUrlFromPanel(state, { logLabel: `步骤 ${visibleStep}` });
await handleStepData(1, refreshResult);
@@ -10490,7 +10516,9 @@ async function getPostStep6AutoRestartDecision(step, error) {
}
async function getLoginAuthStateFromContent(options = {}) {
const { logMessage = '步骤 8:认证页正在切换,等待页面重新就绪后继续确认验证码页状态...' } = options;
const visibleStep = Math.floor(Number(options.visibleStep || options.logStep || options.step) || 0);
const logStep = visibleStep > 0 ? visibleStep : null;
const { logMessage = '认证页正在切换,等待页面重新就绪后继续确认验证码页状态...' } = options;
const result = await sendToContentScriptResilient(
'signup-page',
{
@@ -10503,6 +10531,8 @@ async function getLoginAuthStateFromContent(options = {}) {
retryDelayMs: options.retryDelayMs ?? 600,
responseTimeoutMs: options.responseTimeoutMs ?? (options.timeoutMs ?? 15000),
logMessage,
logStep,
logStepKey: options.logStepKey || '',
}
);
@@ -10553,7 +10583,9 @@ async function ensureStep8VerificationPageReady(options = {}) {
timeoutMs: recoverTimeoutMs,
responseTimeoutMs: recoverTimeoutMs,
retryDelayMs: 700,
logMessage: `步骤 ${visibleStep}认证页进入重试/超时报错状态,正在尝试点击“重试”恢复...`,
logMessage: '认证页进入重试/超时报错状态,正在尝试点击“重试”恢复...',
logStep: visibleStep,
logStepKey: 'fetch-login-code',
}
);
} else if (typeof sendToContentScript === 'function') {
@@ -10567,7 +10599,10 @@ async function ensureStep8VerificationPageReady(options = {}) {
}
recovered = Boolean(recoverResult?.recovered || Number(recoverResult?.clickCount) > 0);
if (recovered && typeof addLog === 'function') {
await addLog(`步骤 ${visibleStep}认证页已点击“重试”,正在重新确认验证码页状态...`, 'warn');
await addLog('认证页已点击“重试”,正在重新确认验证码页状态...', 'warn', {
step: visibleStep,
stepKey: 'fetch-login-code',
});
}
} catch (recoverError) {
const recoverMessage = getErrorMessage(recoverError);
@@ -10575,7 +10610,10 @@ async function ensureStep8VerificationPageReady(options = {}) {
throw recoverError;
}
if (typeof addLog === 'function') {
await addLog(`步骤 ${visibleStep}认证页“重试”恢复失败:${recoverMessage}`, 'warn');
await addLog(`认证页“重试”恢复失败:${recoverMessage}`, 'warn', {
step: visibleStep,
stepKey: 'fetch-login-code',
});
}
}
@@ -10584,7 +10622,8 @@ async function ensureStep8VerificationPageReady(options = {}) {
timeoutMs: 10000,
responseTimeoutMs: 10000,
retryDelayMs: 500,
logMessage: `步骤 ${visibleStep}认证页恢复后,正在确认验证码页是否可继续...`,
logMessage: '认证页恢复后,正在确认验证码页是否可继续...',
logStepKey: 'fetch-login-code',
});
if (pageState.state === 'verification_page' || pageState.state === 'oauth_consent_page') {
return pageState;
@@ -10614,7 +10653,9 @@ async function ensureStep8VerificationPageReady(options = {}) {
async function rerunStep7ForStep8Recovery(options = {}) {
const {
logMessage = '步骤 8:正在回到步骤 7,重新发起登录验证码流程...',
logMessage = '正在回到授权登录步骤,重新发起登录验证码流程...',
logStep = null,
logStepKey = 'fetch-login-code',
postStepDelayMs = 3000,
} = options;
@@ -10623,9 +10664,12 @@ async function rerunStep7ForStep8Recovery(options = {}) {
const authLoginStep = typeof getAuthChainStartStepId === 'function'
? getAuthChainStartStepId(initialState)
: FINAL_OAUTH_CHAIN_START_STEP;
await addLog(logMessage, 'warn');
await addLog(logMessage, 'warn', {
step: logStep,
stepKey: logStepKey,
});
await setStepStatus(authLoginStep, 'running');
await addLog(`步骤 ${authLoginStep} 开始执行`);
await addLog('开始执行', 'info', { step: authLoginStep });
try {
await step7Executor.executeStep7({
@@ -10636,7 +10680,7 @@ async function rerunStep7ForStep8Recovery(options = {}) {
const latestState = await getState();
if (isStopError(err)) {
await setStepStatus(authLoginStep, 'stopped');
await addLog(`步骤 ${authLoginStep} 已被用户停止`, 'warn');
await addLog('已被用户停止', 'warn', { step: authLoginStep });
await appendManualAccountRunRecordIfNeeded(`step${authLoginStep}_stopped`, latestState, getErrorMessage(err));
throw err;
}
@@ -10645,7 +10689,7 @@ async function rerunStep7ForStep8Recovery(options = {}) {
throw new Error(STOP_ERROR_MESSAGE);
}
await setStepStatus(authLoginStep, 'failed');
await addLog(`步骤 ${authLoginStep} 失败:${getErrorMessage(err)}`, 'error');
await addLog(`失败:${getErrorMessage(err)}`, 'error', { step: authLoginStep });
await appendManualAccountRunRecordIfNeeded(`step${authLoginStep}_failed`, latestState, getErrorMessage(err));
throw err;
}
@@ -10752,21 +10796,24 @@ function throwIfStep8SettledOrStopped(isSettled = false) {
}
async function ensureStep8SignupPageReady(tabId, options = {}) {
const visibleStep = Math.floor(Number(options.visibleStep || options.logStep || options.step) || 0);
await ensureContentScriptReadyOnTab('signup-page', tabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
timeoutMs: options.timeoutMs ?? 15000,
retryDelayMs: options.retryDelayMs ?? 600,
logMessage: options.logMessage || '',
logStep: visibleStep > 0 ? visibleStep : null,
logStepKey: options.logStepKey || '',
});
}
async function getStep8PageState(tabId, responseTimeoutMs = 1500) {
async function getStep8PageState(tabId, responseTimeoutMs = 1500, visibleStep = 9) {
try {
const result = await sendTabMessageWithTimeout(tabId, 'signup-page', {
type: 'STEP8_GET_STATE',
source: 'background',
payload: {},
payload: { visibleStep },
}, responseTimeoutMs);
if (result?.error) {
throw new Error(result.error);
@@ -10780,13 +10827,14 @@ async function getStep8PageState(tabId, responseTimeoutMs = 1500) {
}
}
async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS) {
async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS, options = {}) {
const visibleStep = Math.floor(Number(options.visibleStep) || 0) || 9;
const start = Date.now();
let recovered = false;
while (Date.now() - start < timeoutMs) {
throwIfStopped();
const pageState = await getStep8PageState(tabId);
const pageState = await getStep8PageState(tabId, 1500, visibleStep);
if (pageState?.maxCheckAttemptsBlocked) {
throw new Error(`${CLOUDFLARE_SECURITY_BLOCK_ERROR_PREFIX}${CLOUDFLARE_SECURITY_BLOCK_USER_MESSAGE}`);
}
@@ -10796,17 +10844,17 @@ async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS)
const urlPart = pageState?.url ? ` URL: ${pageState.url}` : '';
throw new Error(
pageState?.phoneVerificationPage
? `步骤 9:当前认证页进入手机验证码页,但未开启接码功能,无法继续自动授权。${urlPart}`.trim()
: `步骤 9:当前认证页进入手机号页面,但未开启接码功能,无法继续自动授权。${urlPart}`.trim()
? `步骤 ${visibleStep}:当前认证页进入手机验证码页,但未开启接码功能,无法继续自动授权。${urlPart}`.trim()
: `步骤 ${visibleStep}:当前认证页进入手机号页面,但未开启接码功能,无法继续自动授权。${urlPart}`.trim()
);
}
await phoneVerificationHelpers.completePhoneVerificationFlow(tabId, pageState);
await phoneVerificationHelpers.completePhoneVerificationFlow(tabId, pageState, { visibleStep });
recovered = false;
await sleepWithStop(250);
continue;
}
if (pageState?.addPhonePage) {
throw new Error('步骤 9:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。');
throw new Error(`步骤 ${visibleStep}:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。`);
}
if (pageState?.retryPage) {
const retryUrl = String(pageState?.url || '').trim();
@@ -10816,7 +10864,7 @@ async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS)
|| /\/sign-in-with-chatgpt\/[^/?#]+\/consent(?:[/?#]|$)/i.test(retryUrl)
);
if (!consentLikeRetry) {
throw new Error(`步骤 9:当前认证页已进入重试页,当前流程将直接报错。URL: ${pageState.url || 'unknown'}`);
throw new Error(`步骤 ${visibleStep}:当前认证页已进入重试页,当前流程将直接报错。URL: ${pageState.url || 'unknown'}`);
}
}
if (pageState?.consentReady) {
@@ -10826,7 +10874,9 @@ async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS)
recovered = true;
await ensureStep8SignupPageReady(tabId, {
timeoutMs: Math.min(10000, timeoutMs),
logMessage: '步骤 9:认证页内容脚本已失联,正在等待页面重新就绪...',
visibleStep,
logStepKey: 'confirm-oauth',
logMessage: '认证页内容脚本已失联,正在等待页面重新就绪...',
});
continue;
}
@@ -10834,25 +10884,30 @@ async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS)
await sleepWithStop(250);
}
throw new Error('步骤 9:长时间未进入 OAuth 同意页,无法定位“继续”按钮。');
throw new Error(`步骤 ${visibleStep}:长时间未进入 OAuth 同意页,无法定位“继续”按钮。`);
}
async function prepareStep8DebuggerClick(tabId, options = {}) {
const timeoutMs = options.timeoutMs ?? 15000;
const responseTimeoutMs = options.responseTimeoutMs ?? timeoutMs;
const visibleStep = Math.floor(Number(options.visibleStep) || 0) || 9;
await ensureStep8SignupPageReady(tabId, {
timeoutMs,
logMessage: '步骤 9:认证页内容脚本已失联,正在恢复后继续定位按钮...',
visibleStep,
logStepKey: 'confirm-oauth',
logMessage: '认证页内容脚本已失联,正在恢复后继续定位按钮...',
});
const result = await sendToContentScriptResilient('signup-page', {
type: 'STEP8_FIND_AND_CLICK',
source: 'background',
payload: {},
payload: { visibleStep },
}, {
timeoutMs,
responseTimeoutMs,
retryDelayMs: 600,
logMessage: '步骤 9认证页正在切换,等待 OAuth 同意页按钮重新就绪...',
logMessage: '认证页正在切换,等待 OAuth 同意页按钮重新就绪...',
logStep: visibleStep,
logStepKey: 'confirm-oauth',
});
if (result?.error) {
@@ -10865,14 +10920,18 @@ async function prepareStep8DebuggerClick(tabId, options = {}) {
async function triggerStep8ContentStrategy(tabId, strategy, options = {}) {
const timeoutMs = options.timeoutMs ?? 15000;
const responseTimeoutMs = options.responseTimeoutMs ?? timeoutMs;
const visibleStep = Math.floor(Number(options.visibleStep) || 0) || 9;
await ensureStep8SignupPageReady(tabId, {
timeoutMs,
logMessage: '步骤 9:认证页内容脚本已失联,正在恢复后继续点击“继续”按钮...',
visibleStep,
logStepKey: 'confirm-oauth',
logMessage: '认证页内容脚本已失联,正在恢复后继续点击“继续”按钮...',
});
const result = await sendToContentScriptResilient('signup-page', {
type: 'STEP8_TRIGGER_CONTINUE',
source: 'background',
payload: {
visibleStep,
strategy,
findTimeoutMs: 4000,
enabledTimeoutMs: 3000,
@@ -10881,7 +10940,9 @@ async function triggerStep8ContentStrategy(tabId, strategy, options = {}) {
timeoutMs,
responseTimeoutMs,
retryDelayMs: 600,
logMessage: '步骤 9认证页正在切换,等待“继续”按钮重新就绪...',
logMessage: '认证页正在切换,等待“继续”按钮重新就绪...',
logStep: visibleStep,
logStepKey: 'confirm-oauth',
});
if (result?.error) {
@@ -10895,10 +10956,13 @@ async function recoverAuthRetryPageOnTab(tabId, payload = {}, options = {}) {
const readyTimeoutMs = options.readyTimeoutMs ?? 15000;
const timeoutMs = options.timeoutMs ?? 15000;
const responseTimeoutMs = options.responseTimeoutMs ?? timeoutMs;
const visibleStep = Math.floor(Number(options.visibleStep || payload?.visibleStep || payload?.step) || 0) || 9;
await ensureStep8SignupPageReady(tabId, {
timeoutMs: readyTimeoutMs,
retryDelayMs: options.retryDelayMs ?? 600,
logMessage: options.readyLogMessage || '步骤 9:认证页内容脚本已失联,正在恢复后继续处理重试页...',
visibleStep,
logStepKey: 'confirm-oauth',
logMessage: options.readyLogMessage || '认证页内容脚本已失联,正在恢复后继续处理重试页...',
});
const result = await sendToContentScriptResilient('signup-page', {
type: 'RECOVER_AUTH_RETRY_PAGE',
@@ -10908,7 +10972,9 @@ async function recoverAuthRetryPageOnTab(tabId, payload = {}, options = {}) {
timeoutMs,
responseTimeoutMs,
retryDelayMs: options.retryDelayMs ?? 600,
logMessage: options.logMessage || '步骤 9认证页正在切换,等待“重试”按钮重新就绪...',
logMessage: options.logMessage || '认证页正在切换,等待“重试”按钮重新就绪...',
logStep: visibleStep,
logStepKey: 'confirm-oauth',
});
if (result?.error) {
@@ -10918,9 +10984,10 @@ async function recoverAuthRetryPageOnTab(tabId, payload = {}, options = {}) {
return result;
}
async function reloadStep8ConsentPage(tabId, timeoutMs = 30000) {
async function reloadStep8ConsentPage(tabId, timeoutMs = 30000, options = {}) {
const visibleStep = Math.floor(Number(options.visibleStep) || 0) || 9;
if (!Number.isInteger(tabId)) {
throw new Error('步骤 9:缺少有效的认证页标签页,无法刷新后重试。');
throw new Error(`步骤 ${visibleStep}:缺少有效的认证页标签页,无法刷新后重试。`);
}
await chrome.tabs.update(tabId, { active: true }).catch(() => { });
@@ -10931,7 +10998,7 @@ async function reloadStep8ConsentPage(tabId, timeoutMs = 30000) {
if (settled) return;
settled = true;
chrome.tabs.onUpdated.removeListener(listener);
reject(new Error('步骤 9:刷新认证页后等待页面完成加载超时。'));
reject(new Error(`步骤 ${visibleStep}:刷新认证页后等待页面完成加载超时。`));
}, timeoutMs);
const listener = (updatedTabId, changeInfo) => {
@@ -10956,11 +11023,14 @@ async function reloadStep8ConsentPage(tabId, timeoutMs = 30000) {
await ensureStep8SignupPageReady(tabId, {
timeoutMs: Math.min(15000, timeoutMs),
logMessage: '步骤 9:认证页刷新后内容脚本尚未就绪,正在等待页面恢复...',
visibleStep,
logStepKey: 'confirm-oauth',
logMessage: '认证页刷新后内容脚本尚未就绪,正在等待页面恢复...',
});
}
async function waitForStep8ClickEffect(tabId, baselineUrl, timeoutMs = STEP8_CLICK_EFFECT_TIMEOUT_MS) {
async function waitForStep8ClickEffect(tabId, baselineUrl, timeoutMs = STEP8_CLICK_EFFECT_TIMEOUT_MS, options = {}) {
const visibleStep = Math.floor(Number(options.visibleStep) || 0) || 9;
const start = Date.now();
let recovered = false;
@@ -10969,19 +11039,19 @@ async function waitForStep8ClickEffect(tabId, baselineUrl, timeoutMs = STEP8_CLI
const tab = await chrome.tabs.get(tabId).catch(() => null);
if (!tab) {
throw new Error('步骤 9:认证页面标签页已关闭,无法继续自动授权。');
throw new Error(`步骤 ${visibleStep}:认证页面标签页已关闭,无法继续自动授权。`);
}
if (baselineUrl && typeof tab.url === 'string' && tab.url !== baselineUrl) {
return { progressed: true, reason: 'url_changed', url: tab.url };
}
const pageState = await getStep8PageState(tabId);
const pageState = await getStep8PageState(tabId, 1500, visibleStep);
if (pageState?.maxCheckAttemptsBlocked) {
throw new Error(`${CLOUDFLARE_SECURITY_BLOCK_ERROR_PREFIX}${CLOUDFLARE_SECURITY_BLOCK_USER_MESSAGE}`);
}
if (pageState?.addPhonePage) {
throw new Error('步骤 9:点击“继续”后页面跳到了手机号页面,当前流程无法继续自动授权。');
throw new Error(`步骤 ${visibleStep}:点击“继续”后页面跳到了手机号页面,当前流程无法继续自动授权。`);
}
if (pageState?.retryPage) {
const retryUrl = String(pageState?.url || baselineUrl || '').trim();
@@ -10991,7 +11061,7 @@ async function waitForStep8ClickEffect(tabId, baselineUrl, timeoutMs = STEP8_CLI
|| /\/sign-in-with-chatgpt\/[^/?#]+\/consent(?:[/?#]|$)/i.test(retryUrl)
);
if (!consentLikeRetry) {
throw new Error(`步骤 9:点击“继续”后页面进入认证页重试页,当前流程将直接报错。URL: ${pageState.url || baselineUrl || 'unknown'}`);
throw new Error(`步骤 ${visibleStep}:点击“继续”后页面进入认证页重试页,当前流程将直接报错。URL: ${pageState.url || baselineUrl || 'unknown'}`);
}
}
if (pageState === null) {
@@ -10999,7 +11069,9 @@ async function waitForStep8ClickEffect(tabId, baselineUrl, timeoutMs = STEP8_CLI
recovered = true;
await ensureStep8SignupPageReady(tabId, {
timeoutMs: Math.max(1000, Math.min(8000, timeoutMs)),
logMessage: '步骤 9:点击后认证页正在重载,正在等待内容脚本重新就绪...',
visibleStep,
logStepKey: 'confirm-oauth',
logMessage: '点击后认证页正在重载,正在等待内容脚本重新就绪...',
}).catch(() => null);
continue;
}
@@ -11073,8 +11145,9 @@ async function recoverOAuthLocalhostTimeout(details = {}) {
const loginCodeStep = Number(visibleStep) >= 12 ? 11 : 8;
await addLog(
`步骤 ${visibleStep}检测到 OAuth localhost 回调等待窗口已过期,正在复核认证页并回到步骤 ${authLoginStep} 重拉授权链路。`,
'warn'
`检测到 OAuth localhost 回调等待窗口已过期,正在复核认证页并回到步骤 ${authLoginStep} 重拉授权链路。`,
'warn',
{ step: visibleStep, stepKey: 'confirm-oauth' }
);
let authState = null;
@@ -11082,26 +11155,31 @@ async function recoverOAuthLocalhostTimeout(details = {}) {
authState = await getLoginAuthStateFromContent({
timeoutMs: 10000,
responseTimeoutMs: 10000,
logMessage: `步骤 ${visibleStep}:正在复核认证页状态,确认是否可自动恢复 localhost 回调链路...`,
visibleStep,
logMessage: '正在复核认证页状态,确认是否可自动恢复 localhost 回调链路...',
logStepKey: 'confirm-oauth',
});
} catch (inspectError) {
await addLog(
`步骤 ${visibleStep}复核认证页状态失败(${getErrorMessage(inspectError)}),将先尝试按步骤 ${loginCodeStep} 收尾恢复。`,
'warn'
`复核认证页状态失败(${getErrorMessage(inspectError)}),将先尝试按步骤 ${loginCodeStep} 收尾恢复。`,
'warn',
{ step: visibleStep, stepKey: 'confirm-oauth' }
);
}
if (isAddPhoneAuthState(authState)) {
const stateLabel = getLoginAuthStateLabel(authState.state);
await addLog(
`步骤 ${visibleStep}当前认证页为 ${stateLabel},将直接回到步骤 ${authLoginStep} 重新拉起授权链路,避免步骤 8/9 恢复冲突。`,
'warn'
`当前认证页为 ${stateLabel},将直接回到步骤 ${authLoginStep} 重新拉起授权链路,避免验证码/OAuth 恢复冲突。`,
'warn',
{ step: visibleStep, stepKey: 'confirm-oauth' }
);
} else if (authState && authState.state && !['verification_page', 'oauth_consent_page'].includes(authState.state)) {
const stateLabel = getLoginAuthStateLabel(authState.state);
await addLog(
`步骤 ${visibleStep}当前认证页为 ${stateLabel},不满足快速恢复条件,将回到步骤 ${authLoginStep} 重开授权链路。`,
'warn'
`当前认证页为 ${stateLabel},不满足快速恢复条件,将回到步骤 ${authLoginStep} 重开授权链路。`,
'warn',
{ step: visibleStep, stepKey: 'confirm-oauth' }
);
}
@@ -11111,8 +11189,9 @@ async function recoverOAuthLocalhostTimeout(details = {}) {
}
await addLog(
`步骤 ${visibleStep}正在自动重开步骤 ${authLoginStep} -> ${loginCodeStep},恢复到可继续捕获 localhost 回调的状态。`,
'warn'
`正在自动重开步骤 ${authLoginStep} -> ${loginCodeStep},恢复到可继续捕获 localhost 回调的状态。`,
'warn',
{ step: visibleStep, stepKey: 'confirm-oauth' }
);
await step7Executor.executeStep7({
...latestState,
@@ -11139,8 +11218,9 @@ async function recoverOAuthLocalhostTimeout(details = {}) {
});
await addLog(
`步骤 ${visibleStep}已恢复到步骤 ${authLoginStep} -> ${loginCodeStep} 收尾状态,并刷新 OAuth localhost 回调等待窗口,准备重试当前步骤。`,
'warn'
`已恢复到步骤 ${authLoginStep} -> ${loginCodeStep} 收尾状态,并刷新 OAuth localhost 回调等待窗口,准备重试当前步骤。`,
'warn',
{ step: visibleStep, stepKey: 'confirm-oauth' }
);
return await getState();
}
@@ -11189,20 +11269,32 @@ async function executeStep9(state) {
// ============================================================
async function executeContributionStep10(state) {
const platformVerifyStep = typeof getStepIdByKeyForState === 'function'
? (getStepIdByKeyForState('platform-verify', state) || 10)
: 10;
const confirmOauthStep = typeof getStepIdByKeyForState === 'function'
? (getStepIdByKeyForState('confirm-oauth', state) || 9)
: 9;
const authLoginStep = typeof getStepIdByKeyForState === 'function'
? (getStepIdByKeyForState('oauth-login', state) || 7)
: 7;
if (state.localhostUrl && !isLocalhostOAuthCallbackUrl(state.localhostUrl)) {
throw new Error('步骤 9 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 9。');
throw new Error(`步骤 ${confirmOauthStep} 捕获到的 localhost OAuth 回调地址无效,请重新执行步骤 ${confirmOauthStep}`);
}
if (!state.localhostUrl) {
throw new Error('缺少 localhost 回调地址,请先完成步骤 9。');
throw new Error(`缺少 localhost 回调地址,请先完成步骤 ${confirmOauthStep}`);
}
if (!state.contributionSessionId) {
throw new Error('缺少贡献会话信息,请重新从步骤 7 开始。');
throw new Error(`缺少贡献会话信息,请重新从步骤 ${authLoginStep} 开始。`);
}
if (!contributionOAuthManager?.pollContributionStatus) {
throw new Error('贡献 OAuth 流程尚未接入,无法完成贡献模式的步骤 10。');
throw new Error(`贡献 OAuth 流程尚未接入,无法完成贡献模式的步骤 ${platformVerifyStep}`);
}
await addLog('步骤 10贡献模式正在提交回调并等待最终结果...');
await addLog('贡献模式正在提交回调并等待最终结果...', 'info', {
step: platformVerifyStep,
stepKey: 'platform-verify',
});
let latestState = await getState();
const callbackUrl = latestState.localhostUrl || state.localhostUrl;
@@ -11220,7 +11312,7 @@ async function executeContributionStep10(state) {
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(120000, {
step: 10,
step: platformVerifyStep,
actionLabel: '贡献流程最终结果',
})
: 120000;
@@ -11230,8 +11322,11 @@ async function executeContributionStep10(state) {
const status = String(latestState.contributionStatus || '').trim().toLowerCase();
if (contributionOAuthManager?.isContributionFinalStatus?.(status)) {
if (status === 'auto_approved') {
await addLog(`步骤 10贡献流程已结束,最终状态:${latestState.contributionStatusMessage || status}`, 'ok');
await completeStepFromBackground(10, {
await addLog(`贡献流程已结束,最终状态:${latestState.contributionStatusMessage || status}`, 'ok', {
step: platformVerifyStep,
stepKey: 'platform-verify',
});
await completeStepFromBackground(platformVerifyStep, {
contributionStatus: status,
contributionStatusMessage: latestState.contributionStatusMessage || '',
localhostUrl: callbackUrl,
@@ -11248,12 +11343,15 @@ async function executeContributionStep10(state) {
});
}
throw new Error('步骤 10:等待贡献流程最终结果超时。');
throw new Error(`步骤 ${platformVerifyStep}:等待贡献流程最终结果超时。`);
}
async function executeStep10(state) {
const platformVerifyStep = typeof getStepIdByKeyForState === 'function'
? (getStepIdByKeyForState('platform-verify', state || {}) || 10)
: 10;
if (state?.contributionModeExpected && !state?.contributionMode) {
throw new Error('步骤 10:当前自动流程预期使用贡献模式,但运行态 contributionMode 已丢失,已阻止回退到普通 CPA / SUB2API / Codex2API 提交。请重新进入贡献模式后再点击自动。');
throw new Error(`步骤 ${platformVerifyStep}:当前自动流程预期使用贡献模式,但运行态 contributionMode 已丢失,已阻止回退到普通 CPA / SUB2API / Codex2API 提交。请重新进入贡献模式后再点击自动。`);
}
if (state?.contributionMode) {
return executeContributionStep10(state);