Refactor auto-run flow state preservation

This commit is contained in:
QLHazyCoder
2026-05-18 03:27:30 +08:00
parent e5dc675369
commit feba51da65
21 changed files with 835 additions and 111 deletions
+72 -27
View File
@@ -11,6 +11,7 @@
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
broadcastAutoRunStatus,
broadcastStopToContentScripts,
buildFreshAutoRunKeepState,
cancelPendingCommands,
clearStopRequest,
createAutoRunSessionId,
@@ -77,6 +78,67 @@
throw new Error('自动运行节点执行器未接入。');
}
function buildFreshStartStateSnapshot(state = {}) {
return {
...(state || {}),
currentNodeId: '',
nodeStatuses: {},
stepStatuses: {},
};
}
function resolveFreshStartNodeId(state = {}) {
const freshState = buildFreshStartStateSnapshot(state);
return String(getFirstUnfinishedWorkflowNode(freshState) || '').trim();
}
function buildFreshAttemptKeepState(state = {}, context = {}) {
if (typeof buildFreshAutoRunKeepState === 'function') {
const helperPatch = buildFreshAutoRunKeepState(state, context);
if (helperPatch && typeof helperPatch === 'object' && !Array.isArray(helperPatch)) {
return {
...helperPatch,
};
}
}
return {
activeFlowId: state.activeFlowId,
flowId: state.flowId || state.activeFlowId,
panelMode: state.panelMode,
kiroSourceId: state.kiroSourceId,
vpsUrl: state.vpsUrl,
vpsPassword: state.vpsPassword,
customPassword: state.customPassword,
plusModeEnabled: state.plusModeEnabled,
plusPaymentMethod: state.plusPaymentMethod,
phoneVerificationEnabled: state.phoneVerificationEnabled,
phoneSignupReloginAfterBindEmailEnabled: state.phoneSignupReloginAfterBindEmailEnabled,
paypalEmail: state.paypalEmail,
paypalPassword: state.paypalPassword,
kiroRsUrl: state.kiroRsUrl,
kiroRsKey: state.kiroRsKey,
autoRunSkipFailures: state.autoRunSkipFailures,
autoRunFallbackThreadIntervalMinutes: state.autoRunFallbackThreadIntervalMinutes,
autoRunDelayEnabled: state.autoRunDelayEnabled,
autoRunDelayMinutes: state.autoRunDelayMinutes,
autoStepDelaySeconds: state.autoStepDelaySeconds,
stepExecutionRangeByFlow: state.stepExecutionRangeByFlow,
signupMethod: state.signupMethod,
mailProvider: state.mailProvider,
emailGenerator: state.emailGenerator,
gmailBaseEmail: state.gmailBaseEmail,
mail2925BaseEmail: state.mail2925BaseEmail,
currentMail2925AccountId: state.currentMail2925AccountId,
emailPrefix: state.emailPrefix,
inbucketHost: state.inbucketHost,
inbucketMailbox: state.inbucketMailbox,
cloudflareDomain: state.cloudflareDomain,
cloudflareDomains: state.cloudflareDomains,
reusablePhoneActivation: state.reusablePhoneActivation,
};
}
function createAutoRunRoundSummary(round) {
return {
round,
@@ -502,12 +564,13 @@
autoRunAttemptRun: attemptRun,
});
roundSummary.attempts = attemptRun;
const defaultStartNodeId = typeof runAutoSequenceFromNode === 'function' ? 'open-chatgpt' : 1;
const attemptState = await getState();
const defaultStartNodeId = resolveFreshStartNodeId(attemptState);
let startNodeId = defaultStartNodeId;
let useExistingProgress = false;
if (reuseExistingProgress) {
let currentState = await getState();
let currentState = attemptState;
if (getRunningWorkflowNodes(currentState).length) {
currentState = await waitForRunningWorkflowNodesToFinish({
currentRun: targetRun,
@@ -525,32 +588,14 @@
}
if (!useExistingProgress) {
const prevState = await getState();
const prevState = attemptState;
const keepSettings = {
vpsUrl: prevState.vpsUrl,
vpsPassword: prevState.vpsPassword,
customPassword: prevState.customPassword,
plusModeEnabled: prevState.plusModeEnabled,
paypalEmail: prevState.paypalEmail,
paypalPassword: prevState.paypalPassword,
autoRunSkipFailures: prevState.autoRunSkipFailures,
autoRunFallbackThreadIntervalMinutes: prevState.autoRunFallbackThreadIntervalMinutes,
autoRunDelayEnabled: prevState.autoRunDelayEnabled,
autoRunDelayMinutes: prevState.autoRunDelayMinutes,
autoStepDelaySeconds: prevState.autoStepDelaySeconds,
stepExecutionRangeByFlow: prevState.stepExecutionRangeByFlow,
signupMethod: prevState.signupMethod,
mailProvider: prevState.mailProvider,
emailGenerator: prevState.emailGenerator,
gmailBaseEmail: prevState.gmailBaseEmail,
mail2925BaseEmail: prevState.mail2925BaseEmail,
currentMail2925AccountId: prevState.currentMail2925AccountId,
emailPrefix: prevState.emailPrefix,
inbucketHost: prevState.inbucketHost,
inbucketMailbox: prevState.inbucketMailbox,
cloudflareDomain: prevState.cloudflareDomain,
cloudflareDomains: prevState.cloudflareDomains,
reusablePhoneActivation: prevState.reusablePhoneActivation,
...buildFreshAttemptKeepState(prevState, {
targetRun,
totalRuns,
attemptRun,
sessionId,
}),
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
autoRunSessionId: sessionId,
tabRegistry: {},
+75 -2
View File
@@ -191,6 +191,59 @@
verifyHotmailAccount,
} = deps;
function normalizeMessageFlowId(value = '', fallback = 'openai') {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
if (typeof rootScope.MultiPageFlowRegistry?.normalizeFlowId === 'function') {
return rootScope.MultiPageFlowRegistry.normalizeFlowId(value, fallback);
}
const fallbackFlowId = String(fallback || 'openai').trim().toLowerCase() || 'openai';
const normalized = String(value || '').trim().toLowerCase();
if (!normalized || normalized === 'codex') {
return fallbackFlowId;
}
return normalized;
}
function normalizeMessageSourceId(flowId, sourceId = '', fallback = '') {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
if (typeof rootScope.MultiPageFlowRegistry?.normalizeSourceId === 'function') {
return rootScope.MultiPageFlowRegistry.normalizeSourceId(flowId, sourceId, fallback);
}
const fallbackSourceId = String(
fallback || (normalizeMessageFlowId(flowId) === 'kiro' ? 'kiro-rs' : 'cpa')
).trim().toLowerCase();
return String(sourceId || fallbackSourceId).trim().toLowerCase() || fallbackSourceId;
}
function mapAutoRunSourceIdToPanelMode(sourceId = '', fallback = 'cpa') {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
if (typeof rootScope.MultiPageFlowRegistry?.mapSourceIdToPanelMode === 'function') {
return rootScope.MultiPageFlowRegistry.mapSourceIdToPanelMode('openai', sourceId, fallback);
}
return String(sourceId || fallback || 'cpa').trim().toLowerCase() || 'cpa';
}
function buildAutoRunFlowStateUpdates(payload = {}) {
const hasActiveFlowId = Object.prototype.hasOwnProperty.call(payload, 'activeFlowId');
const hasSourceId = Object.prototype.hasOwnProperty.call(payload, 'sourceId');
if (!hasActiveFlowId && !hasSourceId) {
return {};
}
const activeFlowId = normalizeMessageFlowId(payload.activeFlowId, 'openai');
const updates = {
activeFlowId,
flowId: activeFlowId,
};
if (hasSourceId) {
if (activeFlowId === 'kiro') {
updates.kiroSourceId = normalizeMessageSourceId('kiro', payload.sourceId, 'kiro-rs');
} else {
updates.panelMode = mapAutoRunSourceIdToPanelMode(payload.sourceId, 'cpa');
}
}
return updates;
}
function preserveKeyFromState(updates, currentState, key) {
if (!Object.prototype.hasOwnProperty.call(updates, key)) {
return;
@@ -1208,8 +1261,16 @@
});
}
}
const autoRunFlowStateUpdates = buildAutoRunFlowStateUpdates(message.payload || {});
if (Object.keys(autoRunFlowStateUpdates).length > 0 && typeof setState === 'function') {
await setState(autoRunFlowStateUpdates);
}
const state = await getState();
const autoRunStartValidation = validateAutoRunStart(state, { state });
const autoRunStartValidation = validateAutoRunStart(state, {
activeFlowId: autoRunFlowStateUpdates.activeFlowId ?? state?.activeFlowId,
panelMode: autoRunFlowStateUpdates.panelMode ?? state?.panelMode,
state,
});
if (autoRunStartValidation?.ok === false) {
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
}
@@ -1240,8 +1301,16 @@
});
}
}
const autoRunFlowStateUpdates = buildAutoRunFlowStateUpdates(message.payload || {});
if (Object.keys(autoRunFlowStateUpdates).length > 0 && typeof setState === 'function') {
await setState(autoRunFlowStateUpdates);
}
const state = await getState();
const autoRunStartValidation = validateAutoRunStart(state, { state });
const autoRunStartValidation = validateAutoRunStart(state, {
activeFlowId: autoRunFlowStateUpdates.activeFlowId ?? state?.activeFlowId,
panelMode: autoRunFlowStateUpdates.panelMode ?? state?.panelMode,
state,
});
if (autoRunStartValidation?.ok === false) {
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
}
@@ -1374,6 +1443,10 @@
oauthFlowDeadlineSourceUrl: null,
} : {}),
};
if (Object.prototype.hasOwnProperty.call(updates, 'activeFlowId')
&& !Object.prototype.hasOwnProperty.call(stateUpdates, 'flowId')) {
stateUpdates.flowId = updates.activeFlowId;
}
if (Object.prototype.hasOwnProperty.call(updates, 'icloudHostPreference')) {
const nextHostPreference = String(updates.icloudHostPreference || '').trim().toLowerCase();
stateUpdates.preferredIcloudHost = nextHostPreference === 'icloud.com' || nextHostPreference === 'icloud.com.cn'
+6 -6
View File
@@ -321,13 +321,13 @@
...cloneValue(normalizePlainObject(state.runtimeState)),
};
const activeFlowId = normalizeFlowId(
Object.prototype.hasOwnProperty.call(state, 'flowId')
? state.flowId
: Object.prototype.hasOwnProperty.call(state, 'activeFlowId')
Object.prototype.hasOwnProperty.call(state, 'activeFlowId')
? state.activeFlowId
: Object.prototype.hasOwnProperty.call(baseRuntimeState, 'flowId')
? baseRuntimeState.flowId
: baseRuntimeState.activeFlowId
: Object.prototype.hasOwnProperty.call(state, 'flowId')
? state.flowId
: Object.prototype.hasOwnProperty.call(baseRuntimeState, 'activeFlowId')
? baseRuntimeState.activeFlowId
: baseRuntimeState.flowId
);
const currentNodeId = String(
Object.prototype.hasOwnProperty.call(state, 'currentNodeId')
+3 -3
View File
@@ -326,7 +326,7 @@
try {
const latestState = await getExecutionState(state);
const auth = await startBuilderIdDeviceLogin(
latestState.kiroRegion || DEFAULT_REGION,
DEFAULT_REGION,
fetchImpl
);
const loginUrl = cleanString(auth.verificationUriComplete || auth.verificationUri);
@@ -379,7 +379,7 @@
const clientId = cleanString(latestState.kiroClientId);
const clientSecret = String(latestState.kiroClientSecret || '');
const deviceCode = String(latestState.kiroDeviceAuthorizationCode || '');
const region = normalizeRegion(latestState.kiroAuthRegion || latestState.kiroRegion || DEFAULT_REGION);
const region = normalizeRegion(latestState.kiroAuthRegion, DEFAULT_REGION);
const expiresAt = Math.max(0, Number(latestState.kiroAuthExpiresAt) || 0);
if (!clientId || !clientSecret || !deviceCode) {
throw new Error('Kiro device login has not been started yet.');
@@ -443,7 +443,7 @@
const refreshToken = String(latestState.kiroRefreshToken || '');
const clientId = cleanString(latestState.kiroClientId);
const clientSecret = String(latestState.kiroClientSecret || '');
const region = normalizeRegion(latestState.kiroAuthRegion || latestState.kiroRegion || DEFAULT_REGION);
const region = normalizeRegion(latestState.kiroAuthRegion, DEFAULT_REGION);
const kiroRsUrl = String(latestState.kiroRsUrl || '');
const kiroRsKey = String(latestState.kiroRsKey || '');
if (!refreshToken || !clientId || !clientSecret) {