fix(flow): restore dynamic router handling and downstream reset safety

This commit is contained in:
Hephaestus Bot
2026-04-29 09:57:03 +08:00
parent ba0bf02eeb
commit e322f0ffa6
2 changed files with 152 additions and 106 deletions
+10 -2
View File
@@ -4770,7 +4770,15 @@ function hasSavedProgress(statuses = {}) {
return Object.values({ ...DEFAULT_STATE.stepStatuses, ...statuses }).some((status) => status !== 'pending'); return Object.values({ ...DEFAULT_STATE.stepStatuses, ...statuses }).some((status) => status !== 'pending');
} }
function getDownstreamStateResets(step) { function getDownstreamStateResets(step, state = {}) {
const stepKey = typeof getStepExecutionKeyForState === 'function'
? getStepExecutionKeyForState(step, state)
: String(
typeof getStepDefinitionForState === 'function'
? (getStepDefinitionForState(step, state)?.key || '')
: ''
).trim();
if (step <= 1) { if (step <= 1) {
return { return {
oauthUrl: null, oauthUrl: null,
@@ -4861,7 +4869,7 @@ async function invalidateDownstreamAfterStepRestart(step, options = {}) {
await addLog(`${logLabel},已重置后续步骤状态:${changedSteps.join(', ')}`, 'warn'); await addLog(`${logLabel},已重置后续步骤状态:${changedSteps.join(', ')}`, 'warn');
} }
const resets = getDownstreamStateResets(step); const resets = getDownstreamStateResets(step, state);
if (Object.keys(resets).length) { if (Object.keys(resets).length) {
await setState(resets); await setState(resets);
broadcastDataUpdate(resets); broadcastDataUpdate(resets);
+155 -117
View File
@@ -135,6 +135,7 @@
} }
} }
<<<<<<< HEAD
function resolveLastStepIdForState(state = {}) { function resolveLastStepIdForState(state = {}) {
if (typeof getLastStepIdForState === 'function') { if (typeof getLastStepIdForState === 'function') {
const fromResolver = Number(getLastStepIdForState(state)); const fromResolver = Number(getLastStepIdForState(state));
@@ -147,126 +148,40 @@
: Object.keys(state?.stepStatuses || {}).map((step) => Number(step)).filter(Number.isFinite); : Object.keys(state?.stepStatuses || {}).map((step) => Number(step)).filter(Number.isFinite);
const sorted = stepIds.slice().sort((left, right) => left - right); const sorted = stepIds.slice().sort((left, right) => left - right);
return Math.max(10, sorted[sorted.length - 1] || 0); return Math.max(10, sorted[sorted.length - 1] || 0);
=======
function getStepKeyForState(step, state = {}) {
if (typeof getStepDefinitionForState === 'function') {
return String(getStepDefinitionForState(step, state)?.key || '').trim();
}
return '';
} }
async function handleStepData(step, payload) { function isStepProtectedFromAutoSkip(status) {
if (payload.skipLoginVerificationStep || payload.directOAuthConsentPage) { return status === 'running'
const latestState = await getState(); || status === 'completed'
const currentDefinition = typeof getStepDefinitionForState === 'function' || status === 'manual_completed'
? getStepDefinitionForState(step, latestState) || status === 'skipped';
: null;
const currentKey = String(currentDefinition?.key || '').trim();
if (currentKey === 'oauth-login') {
const stepIds = typeof getStepIdsForState === 'function'
? (getStepIdsForState(latestState) || [])
: Object.keys(latestState?.stepStatuses || {}).map((item) => Number(item)).filter(Number.isFinite);
const ordered = stepIds.slice().sort((left, right) => left - right);
const currentIndex = ordered.indexOf(Number(step));
const nextStep = currentIndex >= 0 ? ordered[currentIndex + 1] : null;
const nextDefinition = Number.isFinite(nextStep) && typeof getStepDefinitionForState === 'function'
? getStepDefinitionForState(nextStep, latestState)
: null;
if (Number.isFinite(nextStep) && String(nextDefinition?.key || '').trim() === 'fetch-login-code') {
const nextStatus = latestState.stepStatuses?.[nextStep];
if (nextStatus !== 'running' && nextStatus !== 'completed' && nextStatus !== 'manual_completed') {
await setStepStatus(nextStep, 'skipped');
await addLog(`步骤 ${step}:检测到已直达 OAuth 授权页,已自动跳过步骤 ${nextStep} 登录验证码。`, 'warn');
}
}
}
} }
switch (step) { function findStepByKeyAfter(currentStep, targetKey, state = {}) {
case 1: { const activeStepIds = typeof getStepIdsForState === 'function'
const updates = {}; ? getStepIdsForState(state)
if (payload.oauthUrl) { : [];
updates.oauthUrl = payload.oauthUrl; const candidates = activeStepIds.length ? activeStepIds : [Number(currentStep) + 1, 8];
broadcastDataUpdate({ oauthUrl: payload.oauthUrl }); return candidates.find((stepId) => {
const numericStep = Number(stepId);
if (!Number.isFinite(numericStep) || numericStep <= Number(currentStep)) {
return false;
} }
if (payload.sub2apiSessionId !== undefined) updates.sub2apiSessionId = payload.sub2apiSessionId || null; const stepKey = getStepKeyForState(numericStep, state);
if (payload.sub2apiOAuthState !== undefined) updates.sub2apiOAuthState = payload.sub2apiOAuthState || null; if (stepKey) {
if (payload.sub2apiGroupId !== undefined) updates.sub2apiGroupId = payload.sub2apiGroupId || null; return stepKey === targetKey;
if (payload.sub2apiDraftName !== undefined) updates.sub2apiDraftName = payload.sub2apiDraftName || null;
if (payload.sub2apiProxyId !== undefined) updates.sub2apiProxyId = payload.sub2apiProxyId || null;
if (payload.cpaOAuthState !== undefined) updates.cpaOAuthState = payload.cpaOAuthState || null;
if (payload.cpaManagementOrigin !== undefined) updates.cpaManagementOrigin = payload.cpaManagementOrigin || null;
if (payload.codex2apiSessionId !== undefined) updates.codex2apiSessionId = payload.codex2apiSessionId || null;
if (payload.codex2apiOAuthState !== undefined) updates.codex2apiOAuthState = payload.codex2apiOAuthState || null;
if (Object.keys(updates).length) {
await setState(updates);
} }
break; return targetKey === 'fetch-login-code' && Number(currentStep) === 7 && numericStep === 8;
}) || null;
} }
case 2:
if (payload.email) { async function handlePlatformVerifyStepData(payload) {
await setEmailState(payload.email);
}
if (payload.skipRegistrationFlow) {
const latestState = await getState();
for (const skipStep of [3, 4, 5]) {
const status = latestState.stepStatuses?.[skipStep];
if (status === 'running' || status === 'completed' || status === 'manual_completed') {
continue;
}
await setStepStatus(skipStep, 'skipped');
}
await addLog('步骤 2:检测到当前已登录会话,已自动跳过步骤 3/4/5,流程将直接进入步骤 6。', 'warn');
break;
}
if (payload.skippedPasswordStep) {
const latestState = await getState();
const step3Status = latestState.stepStatuses?.[3];
if (step3Status !== 'running' && step3Status !== 'completed' && step3Status !== 'manual_completed') {
await setStepStatus(3, 'skipped');
await addLog('步骤 2:提交邮箱后页面直接进入邮箱验证码页,已自动跳过步骤 3。', 'warn');
}
}
break;
case 3:
if (payload.email) await setEmailState(payload.email);
if (payload.signupVerificationRequestedAt) {
await setState({ signupVerificationRequestedAt: payload.signupVerificationRequestedAt });
}
if (payload.loginVerificationRequestedAt) {
await setState({ loginVerificationRequestedAt: payload.loginVerificationRequestedAt });
}
break;
case 7:
if (payload.loginVerificationRequestedAt) {
await setState({ loginVerificationRequestedAt: payload.loginVerificationRequestedAt });
}
break;
case 4:
await setState({
lastEmailTimestamp: payload.emailTimestamp || null,
signupVerificationRequestedAt: null,
});
if (payload.skipProfileStep) {
const latestState = await getState();
const step5Status = latestState.stepStatuses?.[5];
if (step5Status !== 'running' && step5Status !== 'completed' && step5Status !== 'manual_completed') {
await setStepStatus(5, 'skipped');
await addLog('步骤 4:检测到账号已直接进入已登录态,已自动跳过步骤 5。', 'warn');
}
}
break;
case 8:
await setState({
lastEmailTimestamp: payload.emailTimestamp || null,
loginVerificationRequestedAt: null,
});
break;
case 9:
if (payload.localhostUrl) {
if (!isLocalhostOAuthCallbackUrl(payload.localhostUrl)) {
throw new Error('步骤 9 返回了无效的 localhost OAuth 回调地址。');
}
await setState({ localhostUrl: payload.localhostUrl });
broadcastDataUpdate({ localhostUrl: payload.localhostUrl });
}
break;
case 10:
case 13: {
if (payload.localhostUrl) { if (payload.localhostUrl) {
await closeLocalhostCallbackTabs(payload.localhostUrl); await closeLocalhostCallbackTabs(payload.localhostUrl);
} }
@@ -302,8 +217,128 @@
}); });
} }
await finalizeIcloudAliasAfterSuccessfulFlow(latestState); await finalizeIcloudAliasAfterSuccessfulFlow(latestState);
if (typeof finalizePhoneActivationAfterSuccessfulFlow === 'function') {
await finalizePhoneActivationAfterSuccessfulFlow(latestState);
}
>>>>>>> 705c749 (fix(flow): restore dynamic router handling and downstream reset safety)
}
async function handleStepData(step, payload) {
if (step === 1) {
const updates = {};
if (payload.oauthUrl) {
updates.oauthUrl = payload.oauthUrl;
broadcastDataUpdate({ oauthUrl: payload.oauthUrl });
}
if (payload.sub2apiSessionId !== undefined) updates.sub2apiSessionId = payload.sub2apiSessionId || null;
if (payload.sub2apiOAuthState !== undefined) updates.sub2apiOAuthState = payload.sub2apiOAuthState || null;
if (payload.sub2apiGroupId !== undefined) updates.sub2apiGroupId = payload.sub2apiGroupId || null;
if (payload.sub2apiDraftName !== undefined) updates.sub2apiDraftName = payload.sub2apiDraftName || null;
if (payload.sub2apiProxyId !== undefined) updates.sub2apiProxyId = payload.sub2apiProxyId || null;
if (payload.cpaOAuthState !== undefined) updates.cpaOAuthState = payload.cpaOAuthState || null;
if (payload.cpaManagementOrigin !== undefined) updates.cpaManagementOrigin = payload.cpaManagementOrigin || null;
if (payload.codex2apiSessionId !== undefined) updates.codex2apiSessionId = payload.codex2apiSessionId || null;
if (payload.codex2apiOAuthState !== undefined) updates.codex2apiOAuthState = payload.codex2apiOAuthState || null;
if (Object.keys(updates).length) {
await setState(updates);
}
return;
}
const stateForStep = await getState();
const stepKey = getStepKeyForState(step, stateForStep);
if (stepKey === 'oauth-login') {
if (payload.skipLoginVerificationStep) {
await setState({ loginVerificationRequestedAt: null });
const latestState = await getState();
const loginCodeStep = findStepByKeyAfter(step, 'fetch-login-code', latestState);
if (loginCodeStep) {
const currentStatus = latestState.stepStatuses?.[loginCodeStep];
if (!isStepProtectedFromAutoSkip(currentStatus)) {
await setStepStatus(loginCodeStep, 'skipped');
await addLog(`步骤 ${step}:认证页已直接进入 OAuth 授权页,已自动跳过步骤 ${loginCodeStep} 的登录验证码。`, 'warn');
}
}
} else if (payload.loginVerificationRequestedAt) {
await setState({ loginVerificationRequestedAt: payload.loginVerificationRequestedAt });
}
return;
}
if (stepKey === 'fetch-login-code') {
await setState({
lastEmailTimestamp: payload.emailTimestamp || null,
loginVerificationRequestedAt: null,
});
return;
}
if (stepKey === 'confirm-oauth') {
if (payload.localhostUrl) {
if (!isLocalhostOAuthCallbackUrl(payload.localhostUrl)) {
throw new Error(`步骤 ${step} 返回了无效的 localhost OAuth 回调地址。`);
}
await setState({ localhostUrl: payload.localhostUrl });
broadcastDataUpdate({ localhostUrl: payload.localhostUrl });
}
return;
}
if (stepKey === 'platform-verify') {
await handlePlatformVerifyStepData(payload);
return;
}
switch (step) {
case 2:
if (payload.email) {
await setEmailState(payload.email);
}
if (payload.skipRegistrationFlow) {
const latestState = await getState();
for (const skipStep of [3, 4, 5]) {
const status = latestState.stepStatuses?.[skipStep];
if (status === 'running' || status === 'completed' || status === 'manual_completed') {
continue;
}
await setStepStatus(skipStep, 'skipped');
}
await addLog('步骤 2:检测到当前已登录会话,已自动跳过步骤 3/4/5,流程将直接进入步骤 6。', 'warn');
break; break;
} }
if (payload.skippedPasswordStep) {
const latestState = await getState();
const step3Status = latestState.stepStatuses?.[3];
if (step3Status !== 'running' && step3Status !== 'completed' && step3Status !== 'manual_completed') {
await setStepStatus(3, 'skipped');
await addLog('步骤 2:提交邮箱后页面直接进入邮箱验证码页,已自动跳过步骤 3。', 'warn');
}
}
break;
case 3:
if (payload.email) await setEmailState(payload.email);
if (payload.signupVerificationRequestedAt) {
await setState({ signupVerificationRequestedAt: payload.signupVerificationRequestedAt });
}
if (payload.loginVerificationRequestedAt) {
await setState({ loginVerificationRequestedAt: payload.loginVerificationRequestedAt });
}
break;
case 4:
await setState({
lastEmailTimestamp: payload.emailTimestamp || null,
signupVerificationRequestedAt: null,
});
if (payload.skipProfileStep) {
const latestState = await getState();
const step5Status = latestState.stepStatuses?.[5];
if (step5Status !== 'running' && step5Status !== 'completed' && step5Status !== 'manual_completed') {
await setStepStatus(5, 'skipped');
await addLog('步骤 4:检测到账号已直接进入已登录态,已自动跳过步骤 5。', 'warn');
}
}
break;
default: default:
break; break;
} }
@@ -354,9 +389,11 @@
return { ok: true, error: errorMessage }; return { ok: true, error: errorMessage };
} }
const latestState = await getState(); const completionStateCandidate = await getState();
const lastStepId = resolveLastStepIdForState(latestState); const lastStepId = typeof getLastStepIdForState === 'function'
const completionState = message.step === lastStepId ? latestState : null; ? getLastStepIdForState(completionStateCandidate)
: 10;
const completionState = message.step === lastStepId ? completionStateCandidate : null;
await setStepStatus(message.step, 'completed'); await setStepStatus(message.step, 'completed');
await addLog(`步骤 ${message.step} 已完成`, 'ok'); await addLog(`步骤 ${message.step} 已完成`, 'ok');
await handleStepData(message.step, message.payload); await handleStepData(message.step, message.payload);
@@ -509,7 +546,8 @@
await setPersistentSettings({ emailPrefix: message.payload.emailPrefix }); await setPersistentSettings({ emailPrefix: message.payload.emailPrefix });
await setState({ emailPrefix: message.payload.emailPrefix }); await setState({ emailPrefix: message.payload.emailPrefix });
} }
if (doesStepUseCompletionSignal(step)) { const executionState = await getState();
if (doesStepUseCompletionSignal(step, executionState)) {
await executeStepViaCompletionSignal(step); await executeStepViaCompletionSignal(step);
} else { } else {
await executeStep(step); await executeStep(step);