refactor shared flow settings and runtime cleanup

This commit is contained in:
QLHazyCoder
2026-05-24 19:36:08 +08:00
parent c649d43ee6
commit 3a60560e27
40 changed files with 487 additions and 861 deletions
+4 -230
View File
@@ -638,14 +638,11 @@ const IP_PROXY_TARGET_HOST_PATTERNS = [
]; ];
const AUTO_RUN_TIMER_ALARM_NAME = 'auto-run-timer'; const AUTO_RUN_TIMER_ALARM_NAME = 'auto-run-timer';
const IP_PROXY_AUTO_SYNC_ALARM_NAME = 'ip-proxy-auto-sync'; const IP_PROXY_AUTO_SYNC_ALARM_NAME = 'ip-proxy-auto-sync';
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
const AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS = 'between_rounds'; const AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS = 'between_rounds';
const AUTO_RUN_TIMER_KIND_BEFORE_RETRY = 'before_retry'; const AUTO_RUN_TIMER_KIND_BEFORE_RETRY = 'before_retry';
const IP_PROXY_AUTO_SYNC_INTERVAL_MIN_MINUTES = 1; const IP_PROXY_AUTO_SYNC_INTERVAL_MIN_MINUTES = 1;
const IP_PROXY_AUTO_SYNC_INTERVAL_MAX_MINUTES = 1440; const IP_PROXY_AUTO_SYNC_INTERVAL_MAX_MINUTES = 1440;
const IP_PROXY_AUTO_SYNC_DEFAULT_INTERVAL_MINUTES = 15; const IP_PROXY_AUTO_SYNC_DEFAULT_INTERVAL_MINUTES = 15;
const AUTO_RUN_DELAY_MIN_MINUTES = 1;
const AUTO_RUN_DELAY_MAX_MINUTES = 1440;
const AUTO_RUN_RETRY_DELAY_MS = 3000; const AUTO_RUN_RETRY_DELAY_MS = 3000;
const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3; const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3;
const AUTO_STEP_DELAY_MIN_ALLOWED_SECONDS = 0; const AUTO_STEP_DELAY_MIN_ALLOWED_SECONDS = 0;
@@ -1354,9 +1351,7 @@ const PERSISTED_SETTING_DEFAULTS = {
autoRunSkipFailures: false, autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
oauthFlowTimeoutEnabled: true, oauthFlowTimeoutEnabled: true,
autoRunDelayEnabled: false,
operationDelayEnabled: true, operationDelayEnabled: true,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
step6CookieCleanupEnabled: false, step6CookieCleanupEnabled: false,
stepExecutionRangeByFlow: {}, stepExecutionRangeByFlow: {},
@@ -1530,7 +1525,6 @@ const DEFAULT_STATE = {
autoRunAttemptRun: 0, // 当前轮次的重试序号。 autoRunAttemptRun: 0, // 当前轮次的重试序号。
autoRunSessionId: 0, autoRunSessionId: 0,
autoRunRoundSummaries: [], // 自动运行轮次摘要。 autoRunRoundSummaries: [], // 自动运行轮次摘要。
scheduledAutoRunAt: null, // 自动运行计划启动时间戳。
autoRunTimerPlan: null, // 自动运行可恢复计时计划快照。 autoRunTimerPlan: null, // 自动运行可恢复计时计划快照。
autoRunCountdownAt: null, autoRunCountdownAt: null,
autoRunCountdownTitle: '', autoRunCountdownTitle: '',
@@ -1560,17 +1554,6 @@ const DEFAULT_STATE = {
ipProxyAppliedExitSource: '', ipProxyAppliedExitSource: '',
}; };
function normalizeAutoRunDelayMinutes(value) {
const numeric = Number(value);
if (!Number.isFinite(numeric)) {
return PERSISTED_SETTING_DEFAULTS.autoRunDelayMinutes;
}
return Math.min(
AUTO_RUN_DELAY_MAX_MINUTES,
Math.max(AUTO_RUN_DELAY_MIN_MINUTES, Math.floor(numeric))
);
}
function normalizeAutoRunFallbackThreadIntervalMinutes(value) { function normalizeAutoRunFallbackThreadIntervalMinutes(value) {
const rawValue = String(value ?? '').trim(); const rawValue = String(value ?? '').trim();
if (!rawValue) { if (!rawValue) {
@@ -1583,7 +1566,7 @@ function normalizeAutoRunFallbackThreadIntervalMinutes(value) {
} }
return Math.min( return Math.min(
AUTO_RUN_DELAY_MAX_MINUTES, 1440,
Math.max(0, Math.floor(numeric)) Math.max(0, Math.floor(numeric))
); );
} }
@@ -2009,7 +1992,6 @@ function isPhoneSignupIdentityStateForReuse(state = {}) {
const runtimeActive = ( const runtimeActive = (
(typeof isAutoRunLockedState === 'function' && isAutoRunLockedState(state)) (typeof isAutoRunLockedState === 'function' && isAutoRunLockedState(state))
|| (typeof isAutoRunPausedState === 'function' && isAutoRunPausedState(state)) || (typeof isAutoRunPausedState === 'function' && isAutoRunPausedState(state))
|| (typeof isAutoRunScheduledState === 'function' && isAutoRunScheduledState(state))
|| Boolean(state?.autoRunning) || Boolean(state?.autoRunning)
); );
if (!runtimeActive) { if (!runtimeActive) {
@@ -2311,9 +2293,6 @@ function normalizeRunCount(value) {
function normalizeAutoRunTimerKind(value = '') { function normalizeAutoRunTimerKind(value = '') {
const normalized = String(value || '').trim().toLowerCase(); const normalized = String(value || '').trim().toLowerCase();
if (normalized === AUTO_RUN_TIMER_KIND_SCHEDULED_START) {
return AUTO_RUN_TIMER_KIND_SCHEDULED_START;
}
if (normalized === AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS) { if (normalized === AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS) {
return AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS; return AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS;
} }
@@ -2393,22 +2372,6 @@ function normalizeAutoRunTimerPlan(plan) {
const countdownTitle = String(plan.countdownTitle || '').trim(); const countdownTitle = String(plan.countdownTitle || '').trim();
const countdownNote = String(plan.countdownNote || '').trim(); const countdownNote = String(plan.countdownNote || '').trim();
if (kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START) {
return {
kind,
fireAt,
totalRuns,
autoRunSkipFailures,
mode,
currentRun: 0,
attemptRun: 0,
autoRunSessionId,
roundSummaries: [],
countdownTitle: countdownTitle || '已计划自动运行',
countdownNote: countdownNote || `计划于 ${formatAutoRunScheduleTime(fireAt)} 开始`,
};
}
if (kind === AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS) { if (kind === AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS) {
const normalizedCurrentRun = Math.max(1, Math.min(totalRuns, currentRun)); const normalizedCurrentRun = Math.max(1, Math.min(totalRuns, currentRun));
const normalizedAttemptRun = Math.max(1, attemptRun); const normalizedAttemptRun = Math.max(1, attemptRun);
@@ -2449,28 +2412,11 @@ function normalizeAutoRunTimerPlanFromState(state = {}) {
if (directPlan) { if (directPlan) {
return directPlan; return directPlan;
} }
if (state.autoRunPhase !== 'scheduled') {
return null; return null;
} }
const legacyScheduledAt = Number(state.scheduledAutoRunAt);
if (!Number.isFinite(legacyScheduledAt)) {
return null;
}
return normalizeAutoRunTimerPlan({
kind: AUTO_RUN_TIMER_KIND_SCHEDULED_START,
fireAt: legacyScheduledAt,
totalRuns: state.scheduledAutoRunPlan?.totalRuns ?? state.autoRunTotalRuns,
autoRunSkipFailures: state.scheduledAutoRunPlan?.autoRunSkipFailures ?? state.autoRunSkipFailures,
autoRunSessionId: state.autoRunSessionId,
mode: state.scheduledAutoRunPlan?.mode,
});
}
function getAutoRunTimerPlanPhase(kind = '') { function getAutoRunTimerPlanPhase(kind = '') {
return kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START ? 'scheduled' : 'waiting_interval'; return 'waiting_interval';
} }
function getAutoRunTimerStatusPayload(plan) { function getAutoRunTimerStatusPayload(plan) {
@@ -2486,7 +2432,6 @@ function getAutoRunTimerStatusPayload(plan) {
totalRuns: normalizedPlan.totalRuns, totalRuns: normalizedPlan.totalRuns,
attemptRun: normalizedPlan.attemptRun, attemptRun: normalizedPlan.attemptRun,
sessionId: normalizedPlan.autoRunSessionId, sessionId: normalizedPlan.autoRunSessionId,
scheduledAt: phase === 'scheduled' ? normalizedPlan.fireAt : null,
countdownAt: normalizedPlan.fireAt, countdownAt: normalizedPlan.fireAt,
countdownTitle: normalizedPlan.countdownTitle, countdownTitle: normalizedPlan.countdownTitle,
countdownNote: normalizedPlan.countdownNote, countdownNote: normalizedPlan.countdownNote,
@@ -3387,7 +3332,6 @@ function normalizePersistentSettingValue(key, value) {
case 'oauthFlowTimeoutEnabled': case 'oauthFlowTimeoutEnabled':
case 'gopayHelperLocalSmsHelperEnabled': case 'gopayHelperLocalSmsHelperEnabled':
case 'gopayHelperAutoModeEnabled': case 'gopayHelperAutoModeEnabled':
case 'autoRunDelayEnabled':
return Boolean(value); return Boolean(value);
case 'operationDelayEnabled': case 'operationDelayEnabled':
return true; return true;
@@ -3408,8 +3352,6 @@ function normalizePersistentSettingValue(key, value) {
return normalizePhoneSmsProviderOrder(value); return normalizePhoneSmsProviderOrder(value);
case 'autoRunFallbackThreadIntervalMinutes': case 'autoRunFallbackThreadIntervalMinutes':
return normalizeAutoRunFallbackThreadIntervalMinutes(value); return normalizeAutoRunFallbackThreadIntervalMinutes(value);
case 'autoRunDelayMinutes':
return normalizeAutoRunDelayMinutes(value);
case 'autoStepDelaySeconds': case 'autoStepDelaySeconds':
return normalizeAutoStepDelaySeconds(value, PERSISTED_SETTING_DEFAULTS.autoStepDelaySeconds); return normalizeAutoStepDelaySeconds(value, PERSISTED_SETTING_DEFAULTS.autoStepDelaySeconds);
case 'verificationResendCount': case 'verificationResendCount':
@@ -10090,8 +10032,7 @@ function getAutoRunStatusPayload(phase, payload = {}) {
return loggingStatus.getAutoRunStatusPayload(phase, normalizedPayload); return loggingStatus.getAutoRunStatusPayload(phase, normalizedPayload);
} }
return { return {
autoRunning: phase === 'scheduled' autoRunning: phase === 'running'
|| phase === 'running'
|| phase === 'waiting_step' || phase === 'waiting_step'
|| phase === 'waiting_email' || phase === 'waiting_email'
|| phase === 'retrying' || phase === 'retrying'
@@ -10101,7 +10042,6 @@ function getAutoRunStatusPayload(phase, payload = {}) {
autoRunTotalRuns: normalizedPayload.totalRuns ?? 1, autoRunTotalRuns: normalizedPayload.totalRuns ?? 1,
autoRunAttemptRun: normalizedPayload.attemptRun ?? 0, autoRunAttemptRun: normalizedPayload.attemptRun ?? 0,
autoRunSessionId: normalizeAutoRunSessionId(normalizedPayload.sessionId), autoRunSessionId: normalizeAutoRunSessionId(normalizedPayload.sessionId),
scheduledAutoRunAt: Number.isFinite(Number(normalizedPayload.scheduledAt)) ? Number(normalizedPayload.scheduledAt) : null,
autoRunCountdownAt: Number.isFinite(Number(normalizedPayload.countdownAt)) ? Number(normalizedPayload.countdownAt) : null, autoRunCountdownAt: Number.isFinite(Number(normalizedPayload.countdownAt)) ? Number(normalizedPayload.countdownAt) : null,
autoRunCountdownTitle: normalizedPayload.countdownTitle === undefined ? '' : String(normalizedPayload.countdownTitle || ''), autoRunCountdownTitle: normalizedPayload.countdownTitle === undefined ? '' : String(normalizedPayload.countdownTitle || ''),
autoRunCountdownNote: normalizedPayload.countdownNote === undefined ? '' : String(normalizedPayload.countdownNote || ''), autoRunCountdownNote: normalizedPayload.countdownNote === undefined ? '' : String(normalizedPayload.countdownNote || ''),
@@ -10109,9 +10049,6 @@ function getAutoRunStatusPayload(phase, payload = {}) {
} }
async function broadcastAutoRunStatus(phase, payload = {}, extraState = {}) { async function broadcastAutoRunStatus(phase, payload = {}, extraState = {}) {
const rawScheduledAt = phase === 'scheduled'
? (payload.scheduledAt ?? payload.scheduledAutoRunAt ?? null)
: null;
const rawCountdownAt = payload.countdownAt ?? payload.autoRunCountdownAt ?? null; const rawCountdownAt = payload.countdownAt ?? payload.autoRunCountdownAt ?? null;
const statusPayload = { const statusPayload = {
phase, phase,
@@ -10119,7 +10056,6 @@ async function broadcastAutoRunStatus(phase, payload = {}, extraState = {}) {
totalRuns: payload.totalRuns ?? autoRunTotalRuns, totalRuns: payload.totalRuns ?? autoRunTotalRuns,
attemptRun: payload.attemptRun ?? autoRunAttemptRun, attemptRun: payload.attemptRun ?? autoRunAttemptRun,
sessionId: payload.sessionId ?? payload.autoRunSessionId ?? autoRunSessionId, sessionId: payload.sessionId ?? payload.autoRunSessionId ?? autoRunSessionId,
scheduledAt: rawScheduledAt === null ? null : Number(rawScheduledAt),
countdownAt: rawCountdownAt === null ? null : Number(rawCountdownAt), countdownAt: rawCountdownAt === null ? null : Number(rawCountdownAt),
countdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''), countdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
countdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''), countdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''),
@@ -10149,38 +10085,10 @@ function isAutoRunPausedState(state) {
return Boolean(state.autoRunning) && state.autoRunPhase === 'waiting_email'; return Boolean(state.autoRunning) && state.autoRunPhase === 'waiting_email';
} }
function isAutoRunScheduledState(state) {
const plan = normalizeAutoRunTimerPlanFromState(state);
const scheduledAt = state.scheduledAutoRunAt === null ? null : Number(state.scheduledAutoRunAt);
return Boolean(state.autoRunning)
&& state.autoRunPhase === 'scheduled'
&& Number.isFinite(scheduledAt)
&& plan?.kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START;
}
function getPendingAutoRunTimerPlan(state = {}) { function getPendingAutoRunTimerPlan(state = {}) {
return normalizeAutoRunTimerPlanFromState(state); return normalizeAutoRunTimerPlanFromState(state);
} }
function formatAutoRunScheduleTime(timestamp) {
return new Date(timestamp).toLocaleString('zh-CN', {
hour12: false,
timeZone: DISPLAY_TIMEZONE,
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
}
async function setAutoRunDelayEnabledState(enabled) {
const normalized = Boolean(enabled);
await setPersistentSettings({ autoRunDelayEnabled: normalized });
await setState({ autoRunDelayEnabled: normalized });
broadcastDataUpdate({ autoRunDelayEnabled: normalized });
}
async function ensureAutoRunTimerAlarm(fireAt) { async function ensureAutoRunTimerAlarm(fireAt) {
if (!Number.isFinite(fireAt) || fireAt <= Date.now()) { if (!Number.isFinite(fireAt) || fireAt <= Date.now()) {
return false; return false;
@@ -10212,7 +10120,6 @@ async function persistAutoRunTimerPlan(plan, extraState = {}) {
{ {
...extraState, ...extraState,
autoRunTimerPlan: normalizedPlan, autoRunTimerPlan: normalizedPlan,
scheduledAutoRunPlan: null,
} }
); );
await ensureAutoRunTimerAlarm(normalizedPlan.fireAt); await ensureAutoRunTimerAlarm(normalizedPlan.fireAt);
@@ -10225,22 +10132,6 @@ function getAutoRunTimerResumeOptions(plan) {
return null; return null;
} }
if (normalizedPlan.kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START) {
return {
loopOptions: {
autoRunSessionId: normalizedPlan.autoRunSessionId,
autoRunSkipFailures: normalizedPlan.autoRunSkipFailures,
mode: normalizedPlan.mode,
},
statusPayload: {
currentRun: 0,
totalRuns: normalizedPlan.totalRuns,
attemptRun: 0,
sessionId: normalizedPlan.autoRunSessionId,
},
};
}
if (normalizedPlan.kind === AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS) { if (normalizedPlan.kind === AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS) {
const nextRun = Math.min(normalizedPlan.currentRun + 1, normalizedPlan.totalRuns); const nextRun = Math.min(normalizedPlan.currentRun + 1, normalizedPlan.totalRuns);
return { return {
@@ -10314,35 +10205,10 @@ async function launchAutoRunTimerPlan(trigger = 'alarm', options = {}) {
}, { }, {
autoRunRoundSummaries: [], autoRunRoundSummaries: [],
autoRunTimerPlan: null, autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
}); });
return false; return false;
} }
if (plan.kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START) {
const autoRunStartValidation = typeof validateAutoRunStartState === 'function'
? validateAutoRunStartState(state, { state })
: { ok: true, errors: [] };
if (autoRunStartValidation?.ok === false) {
const validationMessage = autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。';
await clearAutoRunTimerAlarm();
await broadcastAutoRunStatus('idle', {
currentRun: 0,
totalRuns: 1,
attemptRun: 0,
}, {
autoRunRoundSummaries: [],
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
});
await addLog(`自动运行计划已取消:${validationMessage}`, 'error');
if (trigger === 'manual') {
throw new Error(validationMessage);
}
return false;
}
}
await clearAutoRunTimerAlarm(); await clearAutoRunTimerAlarm();
if (plan.autoRunSessionId && !isCurrentAutoRunSessionId(plan.autoRunSessionId)) { if (plan.autoRunSessionId && !isCurrentAutoRunSessionId(plan.autoRunSessionId)) {
return false; return false;
@@ -10351,9 +10217,6 @@ async function launchAutoRunTimerPlan(trigger = 'alarm', options = {}) {
autoRunTotalRuns = plan.totalRuns; autoRunTotalRuns = plan.totalRuns;
autoRunAttemptRun = resumeOptions.statusPayload.attemptRun; autoRunAttemptRun = resumeOptions.statusPayload.attemptRun;
autoRunSessionId = normalizeAutoRunSessionId(plan.autoRunSessionId); autoRunSessionId = normalizeAutoRunSessionId(plan.autoRunSessionId);
if (plan.kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START && trigger !== 'manual' && state.autoRunDelayEnabled) {
await setAutoRunDelayEnabledState(false);
}
await broadcastAutoRunStatus( await broadcastAutoRunStatus(
'running', 'running',
resumeOptions.statusPayload, resumeOptions.statusPayload,
@@ -10361,7 +10224,6 @@ async function launchAutoRunTimerPlan(trigger = 'alarm', options = {}) {
autoRunSkipFailures: plan.autoRunSkipFailures, autoRunSkipFailures: plan.autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(plan.totalRuns, plan.roundSummaries), autoRunRoundSummaries: serializeAutoRunRoundSummaries(plan.totalRuns, plan.roundSummaries),
autoRunTimerPlan: null, autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
} }
); );
@@ -10393,81 +10255,12 @@ async function launchAutoRunTimerPlan(trigger = 'alarm', options = {}) {
} }
} }
async function scheduleAutoRun(totalRuns, options = {}) {
const state = await getState();
if (isAutoRunLockedState(state) || isAutoRunPausedState(state) || autoRunActive) {
throw new Error('自动运行已在进行中,请先停止后再重新计划。');
}
if (getPendingAutoRunTimerPlan(state)) {
throw new Error('已有自动运行倒计时计划,请先取消或立即开始。');
}
const delayMinutes = normalizeAutoRunDelayMinutes(options.delayMinutes);
const sessionId = createAutoRunSessionId();
const timerPlan = normalizeAutoRunTimerPlan({
kind: AUTO_RUN_TIMER_KIND_SCHEDULED_START,
fireAt: Date.now() + delayMinutes * 60 * 1000,
totalRuns,
autoRunSkipFailures: options.autoRunSkipFailures,
autoRunSessionId: sessionId,
mode: options.mode,
});
autoRunCurrentRun = 0;
autoRunTotalRuns = timerPlan.totalRuns;
autoRunAttemptRun = 0;
autoRunSessionId = sessionId;
await persistAutoRunTimerPlan(timerPlan, {
autoRunSkipFailures: timerPlan.autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(timerPlan.totalRuns, []),
});
await addLog(
`自动运行已计划:${delayMinutes} 分钟后启动(${formatAutoRunScheduleTime(timerPlan.fireAt)}),目标 ${timerPlan.totalRuns} 轮。`,
'info'
);
return { ok: true, scheduledAt: timerPlan.fireAt };
}
async function cancelScheduledAutoRun(options = {}) {
const state = await getState();
const plan = getPendingAutoRunTimerPlan(state);
if (!plan || plan.kind !== AUTO_RUN_TIMER_KIND_SCHEDULED_START) {
return false;
}
autoRunCurrentRun = 0;
autoRunTotalRuns = plan.totalRuns;
autoRunAttemptRun = 0;
clearCurrentAutoRunSessionId(plan.autoRunSessionId);
await broadcastAutoRunStatus(
'idle',
{
currentRun: 0,
totalRuns: plan.totalRuns,
attemptRun: 0,
sessionId: 0,
},
{
autoRunSessionId: 0,
autoRunRoundSummaries: [],
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
}
);
await clearAutoRunTimerAlarm();
if (options.logMessage !== false) {
await addLog(options.logMessage || '已取消自动运行倒计时计划。', 'warn');
}
return true;
}
async function restoreAutoRunTimerIfNeeded() { async function restoreAutoRunTimerIfNeeded() {
const state = await getState(); const state = await getState();
let plan = getPendingAutoRunTimerPlan(state); let plan = getPendingAutoRunTimerPlan(state);
if (!plan) { if (!plan) {
clearCurrentAutoRunSessionId(); clearCurrentAutoRunSessionId();
if (state.autoRunPhase === 'scheduled' || state.autoRunPhase === 'waiting_interval') { if (state.autoRunPhase === 'waiting_interval') {
await clearAutoRunTimerAlarm(); await clearAutoRunTimerAlarm();
await broadcastAutoRunStatus('idle', { await broadcastAutoRunStatus('idle', {
currentRun: 0, currentRun: 0,
@@ -10478,7 +10271,6 @@ async function restoreAutoRunTimerIfNeeded() {
autoRunSessionId: 0, autoRunSessionId: 0,
autoRunRoundSummaries: [], autoRunRoundSummaries: [],
autoRunTimerPlan: null, autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
}); });
} }
return; return;
@@ -10511,7 +10303,6 @@ async function restoreAutoRunTimerIfNeeded() {
autoRunSkipFailures: plan.autoRunSkipFailures, autoRunSkipFailures: plan.autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(plan.totalRuns, plan.roundSummaries), autoRunRoundSummaries: serializeAutoRunRoundSummaries(plan.totalRuns, plan.roundSummaries),
autoRunTimerPlan: plan, autoRunTimerPlan: plan,
scheduledAutoRunPlan: null,
} }
); );
await ensureAutoRunTimerAlarm(plan.fireAt); await ensureAutoRunTimerAlarm(plan.fireAt);
@@ -10526,9 +10317,6 @@ async function ensureManualInteractionAllowed(actionLabel) {
if (isAutoRunPausedState(state)) { if (isAutoRunPausedState(state)) {
throw new Error(`自动流程当前已暂停。请点击“继续”,或先确认接管自动流程后再${actionLabel}`); throw new Error(`自动流程当前已暂停。请点击“继续”,或先确认接管自动流程后再${actionLabel}`);
} }
if (isAutoRunScheduledState(state)) {
throw new Error(`自动流程已计划启动。请先取消计划,或立即开始后再${actionLabel}`);
}
return state; return state;
} }
@@ -11495,15 +11283,6 @@ async function requestStop(options = {}) {
const inferredStopNode = inferStoppedRecordNode(state); const inferredStopNode = inferStoppedRecordNode(state);
const timerPlan = getPendingAutoRunTimerPlan(state); const timerPlan = getPendingAutoRunTimerPlan(state);
if (timerPlan?.kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START && !autoRunActive) {
await cancelScheduledAutoRun({
logMessage: options.logMessage === false
? false
: (options.logMessage || '已取消自动运行倒计时计划。'),
});
return;
}
if (timerPlan && !autoRunActive) { if (timerPlan && !autoRunActive) {
autoRunCurrentRun = timerPlan.currentRun; autoRunCurrentRun = timerPlan.currentRun;
autoRunTotalRuns = timerPlan.totalRuns; autoRunTotalRuns = timerPlan.totalRuns;
@@ -11522,7 +11301,6 @@ async function requestStop(options = {}) {
autoRunSkipFailures: timerPlan.autoRunSkipFailures, autoRunSkipFailures: timerPlan.autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(timerPlan.totalRuns, timerPlan.roundSummaries), autoRunRoundSummaries: serializeAutoRunRoundSummaries(timerPlan.totalRuns, timerPlan.roundSummaries),
autoRunTimerPlan: null, autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
}); });
await clearAutoRunTimerAlarm(); await clearAutoRunTimerAlarm();
clearStopRequest(); clearStopRequest();
@@ -11582,7 +11360,6 @@ async function requestStop(options = {}) {
}, { }, {
autoRunSessionId: 0, autoRunSessionId: 0,
autoRunTimerPlan: null, autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
}); });
} }
@@ -14153,7 +13930,6 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
buildPersistentSettingsPayload, buildPersistentSettingsPayload,
broadcastDataUpdate, broadcastDataUpdate,
applyIpProxySettingsFromState, applyIpProxySettingsFromState,
cancelScheduledAutoRun,
checkIcloudSession, checkIcloudSession,
clearAccountRunHistory: (...args) => clearAndBroadcastAccountRunHistory(...args), clearAccountRunHistory: (...args) => clearAndBroadcastAccountRunHistory(...args),
deleteAccountRunHistoryRecords: (...args) => deleteAndBroadcastAccountRunHistoryRecords(...args), deleteAccountRunHistoryRecords: (...args) => deleteAndBroadcastAccountRunHistoryRecords(...args),
@@ -14260,7 +14036,6 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
normalizeMail2925Accounts, normalizeMail2925Accounts,
normalizePayPalAccounts, normalizePayPalAccounts,
normalizeRunCount, normalizeRunCount,
AUTO_RUN_TIMER_KIND_SCHEDULED_START,
notifyNodeComplete, notifyNodeComplete,
notifyNodeError, notifyNodeError,
patchHotmailAccount, patchHotmailAccount,
@@ -14270,7 +14045,6 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
probeIpProxyExit, probeIpProxyExit,
resetState, resetState,
resumeAutoRun, resumeAutoRun,
scheduleAutoRun,
selectLuckmailPurchase, selectLuckmailPurchase,
switchIpProxy, switchIpProxy,
changeIpProxyExit, changeIpProxyExit,
-4
View File
@@ -158,8 +158,6 @@
kiroRsKey: state.kiroRsKey, kiroRsKey: state.kiroRsKey,
autoRunSkipFailures: state.autoRunSkipFailures, autoRunSkipFailures: state.autoRunSkipFailures,
autoRunFallbackThreadIntervalMinutes: state.autoRunFallbackThreadIntervalMinutes, autoRunFallbackThreadIntervalMinutes: state.autoRunFallbackThreadIntervalMinutes,
autoRunDelayEnabled: state.autoRunDelayEnabled,
autoRunDelayMinutes: state.autoRunDelayMinutes,
autoStepDelaySeconds: state.autoStepDelaySeconds, autoStepDelaySeconds: state.autoStepDelaySeconds,
stepExecutionRangeByFlow: state.stepExecutionRangeByFlow, stepExecutionRangeByFlow: state.stepExecutionRangeByFlow,
signupMethod: state.signupMethod, signupMethod: state.signupMethod,
@@ -502,7 +500,6 @@
}, { }, {
autoRunSessionId: 0, autoRunSessionId: 0,
autoRunTimerPlan: null, autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
}); });
clearStopRequest(); clearStopRequest();
} }
@@ -1159,7 +1156,6 @@
autoRunSessionId: 0, autoRunSessionId: 0,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries), autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
autoRunTimerPlan: null, autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
...getAutoRunStatusPayload(deps.getStopRequested() || stoppedEarly ? 'stopped' : 'complete', { ...getAutoRunStatusPayload(deps.getStopRequested() || stoppedEarly ? 'stopped' : 'complete', {
currentRun: deps.getStopRequested() || stoppedEarly ? afterRuntime.autoRunCurrentRun : afterRuntime.autoRunTotalRuns, currentRun: deps.getStopRequested() || stoppedEarly ? afterRuntime.autoRunCurrentRun : afterRuntime.autoRunTotalRuns,
totalRuns: afterRuntime.autoRunTotalRuns, totalRuns: afterRuntime.autoRunTotalRuns,
+1 -3
View File
@@ -209,8 +209,7 @@
function getAutoRunStatusPayload(phase, payload = {}) { function getAutoRunStatusPayload(phase, payload = {}) {
return { return {
autoRunning: phase === 'scheduled' autoRunning: phase === 'running'
|| phase === 'running'
|| phase === 'waiting_step' || phase === 'waiting_step'
|| phase === 'waiting_email' || phase === 'waiting_email'
|| phase === 'retrying' || phase === 'retrying'
@@ -220,7 +219,6 @@
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
autoRunAttemptRun: payload.attemptRun ?? 0, autoRunAttemptRun: payload.attemptRun ?? 0,
autoRunSessionId: Math.max(0, Math.floor(Number(payload.sessionId ?? payload.autoRunSessionId) || 0)), autoRunSessionId: Math.max(0, Math.floor(Number(payload.sessionId ?? payload.autoRunSessionId) || 0)),
scheduledAutoRunAt: Number.isFinite(Number(payload.scheduledAt)) ? Number(payload.scheduledAt) : null,
autoRunCountdownAt: Number.isFinite(Number(payload.countdownAt)) ? Number(payload.countdownAt) : null, autoRunCountdownAt: Number.isFinite(Number(payload.countdownAt)) ? Number(payload.countdownAt) : null,
autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''), autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
autoRunCountdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''), autoRunCountdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''),
+1 -66
View File
@@ -11,7 +11,6 @@
buildPersistentSettingsPayload, buildPersistentSettingsPayload,
broadcastDataUpdate, broadcastDataUpdate,
applyIpProxySettingsFromState, applyIpProxySettingsFromState,
cancelScheduledAutoRun,
checkIcloudSession, checkIcloudSession,
clearAccountRunHistory, clearAccountRunHistory,
deleteAccountRunHistoryRecords, deleteAccountRunHistoryRecords,
@@ -145,7 +144,6 @@
normalizeMail2925Accounts, normalizeMail2925Accounts,
normalizePayPalAccounts, normalizePayPalAccounts,
normalizeRunCount, normalizeRunCount,
AUTO_RUN_TIMER_KIND_SCHEDULED_START,
notifyNodeComplete, notifyNodeComplete,
notifyNodeError, notifyNodeError,
patchMail2925Account, patchMail2925Account,
@@ -158,7 +156,6 @@
handleCloudflareSecurityBlocked, handleCloudflareSecurityBlocked,
resetState, resetState,
resumeAutoRun, resumeAutoRun,
scheduleAutoRun,
selectLuckmailPurchase, selectLuckmailPurchase,
switchIpProxy, switchIpProxy,
changeIpProxyExit, changeIpProxyExit,
@@ -1324,7 +1321,7 @@
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。'); throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
} }
if (getPendingAutoRunTimerPlan(state)) { if (getPendingAutoRunTimerPlan(state)) {
throw new Error('已有自动运行倒计时计划,请先取消或立即开始。'); throw new Error('已有线程间隔等待,请先停止或立即继续。');
} }
const totalRuns = normalizeRunCount(message.payload?.totalRuns || 1); const totalRuns = normalizeRunCount(message.payload?.totalRuns || 1);
const autoRunSkipFailures = Boolean(message.payload?.autoRunSkipFailures); const autoRunSkipFailures = Boolean(message.payload?.autoRunSkipFailures);
@@ -1334,68 +1331,6 @@
return { ok: true }; return { ok: true };
} }
case 'SCHEDULE_AUTO_RUN': {
clearStopRequest();
if (message.source === 'sidepanel') {
await lockAutomationWindowFromMessage(message, sender);
}
if (Boolean(message.payload?.accountContributionEnabled) && typeof setAccountContributionMode === 'function') {
await setAccountContributionMode(true, {
adapterId: message.payload?.contributionAdapterId,
flowId: message.payload?.activeFlowId || message.payload?.flowId,
});
if (typeof setState === 'function') {
const contributionNickname = String(message.payload?.contributionNickname || '').trim();
const contributionQq = String(message.payload?.contributionQq || '').trim();
await setState({
contributionNickname,
contributionQq,
});
}
}
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, {
activeFlowId: autoRunFlowStateUpdates.activeFlowId ?? state?.activeFlowId,
targetId: autoRunFlowStateUpdates.targetId ?? state?.targetId,
state,
});
if (autoRunStartValidation?.ok === false) {
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
}
const totalRuns = normalizeRunCount(message.payload?.totalRuns || 1);
return await scheduleAutoRun(totalRuns, {
delayMinutes: message.payload?.delayMinutes,
autoRunSkipFailures: Boolean(message.payload?.autoRunSkipFailures),
mode: message.payload?.mode,
});
}
case 'START_SCHEDULED_AUTO_RUN_NOW': {
clearStopRequest();
if (message.source === 'sidepanel') {
await lockAutomationWindowFromMessage(message, sender);
}
const started = await launchAutoRunTimerPlan('manual', {
expectedKinds: [AUTO_RUN_TIMER_KIND_SCHEDULED_START],
});
if (!started) {
throw new Error('当前没有可立即开始的倒计时计划。');
}
return { ok: true };
}
case 'CANCEL_SCHEDULED_AUTO_RUN': {
const cancelled = await cancelScheduledAutoRun();
if (!cancelled) {
throw new Error('当前没有可取消的倒计时计划。');
}
return { ok: true };
}
case 'SKIP_AUTO_RUN_COUNTDOWN': { case 'SKIP_AUTO_RUN_COUNTDOWN': {
clearStopRequest(); clearStopRequest();
if (message.source === 'sidepanel') { if (message.source === 'sidepanel') {
+14
View File
@@ -49,6 +49,20 @@
label: 'IP \u4ee3\u7406', label: 'IP \u4ee3\u7406',
sectionIds: ['ip-proxy-section'], sectionIds: ['ip-proxy-section'],
}, },
'shared-auto-run': {
id: 'shared-auto-run',
label: '\u81ea\u52a8\u8fd0\u884c',
rowIds: [
'row-shared-auto-run',
'row-auto-run-thread-interval',
'row-step-execution-range',
],
},
'shared-settings-actions': {
id: 'shared-settings-actions',
label: '\u8bbe\u7f6e\u64cd\u4f5c',
rowIds: ['row-settings-actions'],
},
}); });
function buildFlowDefinitions() { function buildFlowDefinitions() {
+1 -3
View File
@@ -209,8 +209,7 @@
function getAutoRunStatusPayload(phase, payload = {}) { function getAutoRunStatusPayload(phase, payload = {}) {
return { return {
autoRunning: phase === 'scheduled' autoRunning: phase === 'running'
|| phase === 'running'
|| phase === 'waiting_step' || phase === 'waiting_step'
|| phase === 'waiting_email' || phase === 'waiting_email'
|| phase === 'retrying' || phase === 'retrying'
@@ -220,7 +219,6 @@
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
autoRunAttemptRun: payload.attemptRun ?? 0, autoRunAttemptRun: payload.attemptRun ?? 0,
autoRunSessionId: Math.max(0, Math.floor(Number(payload.sessionId ?? payload.autoRunSessionId) || 0)), autoRunSessionId: Math.max(0, Math.floor(Number(payload.sessionId ?? payload.autoRunSessionId) || 0)),
scheduledAutoRunAt: Number.isFinite(Number(payload.scheduledAt)) ? Number(payload.scheduledAt) : null,
autoRunCountdownAt: Number.isFinite(Number(payload.countdownAt)) ? Number(payload.countdownAt) : null, autoRunCountdownAt: Number.isFinite(Number(payload.countdownAt)) ? Number(payload.countdownAt) : null,
autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''), autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
autoRunCountdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''), autoRunCountdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''),
+44 -70
View File
@@ -115,48 +115,11 @@
if (isPlainObject(range)) { if (isPlainObject(range)) {
return normalizeStepExecutionRangeEntry(range, { enabled: false, fromStep: 1, toStep: 1 }); return normalizeStepExecutionRangeEntry(range, { enabled: false, fromStep: 1, toStep: 1 });
} }
if (flowId === 'openai') {
return { enabled: false, fromStep: 1, toStep: 11 };
}
if (flowId === 'kiro') {
return { enabled: false, fromStep: 1, toStep: 9 };
}
const lastStep = Math.max(1, Number(getFlowDefinition(flowId)?.workflowStepCount) || 1); const lastStep = Math.max(1, Number(getFlowDefinition(flowId)?.workflowStepCount) || 1);
return { enabled: false, fromStep: 1, toStep: lastStep }; return { enabled: false, fromStep: 1, toStep: lastStep };
} }
function buildDefaultTargetState(flowId, targetId) { function buildDefaultTargetState(flowId, targetId) {
if (flowId === 'openai' && targetId === 'cpa') {
return {
vpsUrl: '',
vpsPassword: '',
localCpaStep9Mode: 'submit',
};
}
if (flowId === 'openai' && targetId === 'sub2api') {
return {
sub2apiUrl: '',
sub2apiEmail: '',
sub2apiPassword: '',
sub2apiGroupName: 'codex',
sub2apiGroupNames: ['codex', 'openai-plus'],
sub2apiAccountPriority: 1,
sub2apiDefaultProxyName: '',
};
}
if (flowId === 'openai' && targetId === 'codex2api') {
return {
codex2apiUrl: '',
codex2apiAdminKey: '',
};
}
if (flowId === 'kiro' && targetId === 'kiro-rs') {
return {
baseUrl: defaultKiroRsUrl,
apiKey: '',
};
}
const definition = getFlowDefinition(flowId) || {}; const definition = getFlowDefinition(flowId) || {};
const flowDefaults = getFlowSettingsDefaults(flowId); const flowDefaults = getFlowSettingsDefaults(flowId);
const targetDefaults = isPlainObject(flowDefaults?.targets?.[targetId]) const targetDefaults = isPlainObject(flowDefaults?.targets?.[targetId])
@@ -200,24 +163,7 @@
stepExecutionRange: getDefaultStepExecutionRange(flowId), stepExecutionRange: getDefaultStepExecutionRange(flowId),
}, },
}; };
if (flowId === 'openai') { return mergePlainObjects(base, getFlowSettingsDefaults(flowId));
return mergePlainObjects(base, {
signup: {
signupMethod: 'email',
phoneVerificationEnabled: false,
phoneSignupReloginAfterBindEmailEnabled: false,
},
plus: {
plusModeEnabled: false,
plusPaymentMethod: 'paypal-hosted',
plusAccountAccessStrategy: 'oauth',
hostedCheckoutVerificationUrl: '',
hostedCheckoutPhoneNumber: '',
plusHostedCheckoutOauthDelaySeconds: 3,
},
});
}
return base;
} }
function buildDefaultFlows() { function buildDefaultFlows() {
@@ -364,6 +310,18 @@
} }
function normalizeOpenAiSettings(input = {}, nested = {}, defaults = {}, currentFlow = {}) { function normalizeOpenAiSettings(input = {}, nested = {}, defaults = {}, currentFlow = {}) {
const defaultOpenAiFlow = isPlainObject(defaults?.flows?.openai)
? defaults.flows.openai
: {};
const defaultOpenAiTargets = isPlainObject(defaultOpenAiFlow.targets)
? defaultOpenAiFlow.targets
: {};
const defaultOpenAiSignup = isPlainObject(defaultOpenAiFlow.signup)
? defaultOpenAiFlow.signup
: {};
const defaultOpenAiPlus = isPlainObject(defaultOpenAiFlow.plus)
? defaultOpenAiFlow.plus
: {};
const cpaSource = { const cpaSource = {
...currentFlow.targets.cpa, ...currentFlow.targets.cpa,
...getTargetValue( ...getTargetValue(
@@ -407,66 +365,82 @@
...currentFlow, ...currentFlow,
targets: { targets: {
...currentFlow.targets, ...currentFlow.targets,
cpa: normalizeFlowTargetState('openai', 'cpa', cpaSource, defaults.flows.openai.targets.cpa), cpa: normalizeFlowTargetState('openai', 'cpa', cpaSource, defaultOpenAiTargets.cpa || {}),
sub2api: normalizeFlowTargetState('openai', 'sub2api', sub2apiSource, defaults.flows.openai.targets.sub2api), sub2api: normalizeFlowTargetState('openai', 'sub2api', sub2apiSource, defaultOpenAiTargets.sub2api || {}),
codex2api: normalizeFlowTargetState('openai', 'codex2api', codex2apiSource, defaults.flows.openai.targets.codex2api), codex2api: normalizeFlowTargetState('openai', 'codex2api', codex2apiSource, defaultOpenAiTargets.codex2api || {}),
}, },
signup: { signup: {
signupMethod: String( signupMethod: String(
input?.signupMethod input?.signupMethod
?? currentFlow.signup?.signupMethod ?? currentFlow.signup?.signupMethod
?? defaults.flows.openai.signup.signupMethod ?? defaultOpenAiSignup.signupMethod
?? 'email'
).trim().toLowerCase() === 'phone' ? 'phone' : 'email', ).trim().toLowerCase() === 'phone' ? 'phone' : 'email',
phoneVerificationEnabled: Boolean( phoneVerificationEnabled: Boolean(
input?.phoneVerificationEnabled input?.phoneVerificationEnabled
?? currentFlow.signup?.phoneVerificationEnabled ?? currentFlow.signup?.phoneVerificationEnabled
?? defaults.flows.openai.signup.phoneVerificationEnabled ?? defaultOpenAiSignup.phoneVerificationEnabled
?? false
), ),
phoneSignupReloginAfterBindEmailEnabled: Boolean( phoneSignupReloginAfterBindEmailEnabled: Boolean(
input?.phoneSignupReloginAfterBindEmailEnabled input?.phoneSignupReloginAfterBindEmailEnabled
?? currentFlow.signup?.phoneSignupReloginAfterBindEmailEnabled ?? currentFlow.signup?.phoneSignupReloginAfterBindEmailEnabled
?? defaults.flows.openai.signup.phoneSignupReloginAfterBindEmailEnabled ?? defaultOpenAiSignup.phoneSignupReloginAfterBindEmailEnabled
?? false
), ),
}, },
plus: { plus: {
plusModeEnabled: Boolean( plusModeEnabled: Boolean(
input?.plusModeEnabled input?.plusModeEnabled
?? currentFlow.plus?.plusModeEnabled ?? currentFlow.plus?.plusModeEnabled
?? defaults.flows.openai.plus.plusModeEnabled ?? defaultOpenAiPlus.plusModeEnabled
?? false
), ),
plusPaymentMethod: String( plusPaymentMethod: String(
input?.plusPaymentMethod input?.plusPaymentMethod
?? currentFlow.plus?.plusPaymentMethod ?? currentFlow.plus?.plusPaymentMethod
?? defaults.flows.openai.plus.plusPaymentMethod ?? defaultOpenAiPlus.plusPaymentMethod
).trim() || defaults.flows.openai.plus.plusPaymentMethod, ?? 'paypal-hosted'
).trim() || defaultOpenAiPlus.plusPaymentMethod || 'paypal-hosted',
plusAccountAccessStrategy: normalizePlusAccountAccessStrategy( plusAccountAccessStrategy: normalizePlusAccountAccessStrategy(
input?.plusAccountAccessStrategy input?.plusAccountAccessStrategy
?? currentFlow.plus?.plusAccountAccessStrategy ?? currentFlow.plus?.plusAccountAccessStrategy
?? defaults.flows.openai.plus.plusAccountAccessStrategy ?? defaultOpenAiPlus.plusAccountAccessStrategy
?? 'oauth'
), ),
hostedCheckoutVerificationUrl: String( hostedCheckoutVerificationUrl: String(
input?.hostedCheckoutVerificationUrl input?.hostedCheckoutVerificationUrl
?? currentFlow.plus?.hostedCheckoutVerificationUrl ?? currentFlow.plus?.hostedCheckoutVerificationUrl
?? defaults.flows.openai.plus.hostedCheckoutVerificationUrl ?? defaultOpenAiPlus.hostedCheckoutVerificationUrl
?? ''
).trim(), ).trim(),
hostedCheckoutPhoneNumber: String( hostedCheckoutPhoneNumber: String(
input?.hostedCheckoutPhoneNumber input?.hostedCheckoutPhoneNumber
?? currentFlow.plus?.hostedCheckoutPhoneNumber ?? currentFlow.plus?.hostedCheckoutPhoneNumber
?? defaults.flows.openai.plus.hostedCheckoutPhoneNumber ?? defaultOpenAiPlus.hostedCheckoutPhoneNumber
?? ''
).trim(), ).trim(),
plusHostedCheckoutOauthDelaySeconds: (() => { plusHostedCheckoutOauthDelaySeconds: (() => {
const numeric = Number( const numeric = Number(
input?.plusHostedCheckoutOauthDelaySeconds input?.plusHostedCheckoutOauthDelaySeconds
?? currentFlow.plus?.plusHostedCheckoutOauthDelaySeconds ?? currentFlow.plus?.plusHostedCheckoutOauthDelaySeconds
?? defaults.flows.openai.plus.plusHostedCheckoutOauthDelaySeconds ?? defaultOpenAiPlus.plusHostedCheckoutOauthDelaySeconds
?? 3
); );
return Math.min(120, Math.max(0, Math.floor(Number.isFinite(numeric) ? numeric : defaults.flows.openai.plus.plusHostedCheckoutOauthDelaySeconds))); const fallback = Number(defaultOpenAiPlus.plusHostedCheckoutOauthDelaySeconds ?? 3) || 3;
return Math.min(120, Math.max(0, Math.floor(Number.isFinite(numeric) ? numeric : fallback)));
})(), })(),
}, },
}; };
} }
function normalizeKiroSettings(input = {}, defaults = {}, currentFlow = {}) { function normalizeKiroSettings(input = {}, defaults = {}, currentFlow = {}) {
const defaultKiroFlow = isPlainObject(defaults?.flows?.kiro)
? defaults.flows.kiro
: {};
const defaultKiroTargets = isPlainObject(defaultKiroFlow.targets)
? defaultKiroFlow.targets
: {};
const targetSource = { const targetSource = {
...currentFlow.targets['kiro-rs'], ...currentFlow.targets['kiro-rs'],
baseUrl: input?.kiroRsUrl ?? input?.kiroRsBaseUrl ?? currentFlow.targets['kiro-rs'].baseUrl, baseUrl: input?.kiroRsUrl ?? input?.kiroRsBaseUrl ?? currentFlow.targets['kiro-rs'].baseUrl,
@@ -476,7 +450,7 @@
...currentFlow, ...currentFlow,
targets: { targets: {
...currentFlow.targets, ...currentFlow.targets,
'kiro-rs': normalizeFlowTargetState('kiro', 'kiro-rs', targetSource, defaults.flows.kiro.targets['kiro-rs']), 'kiro-rs': normalizeFlowTargetState('kiro', 'kiro-rs', targetSource, defaultKiroTargets['kiro-rs'] || {}),
}, },
}; };
} }
+67 -1
View File
@@ -4,6 +4,8 @@
const GROK_SIGNUP_URL = 'https://accounts.x.ai/sign-up?redirect=grok-com'; const GROK_SIGNUP_URL = 'https://accounts.x.ai/sign-up?redirect=grok-com';
const GROK_REGISTER_PAGE_SOURCE_ID = 'grok-register-page'; const GROK_REGISTER_PAGE_SOURCE_ID = 'grok-register-page';
const DEFAULT_GROK_PAGE_TIMEOUT_MS = 90 * 1000; const DEFAULT_GROK_PAGE_TIMEOUT_MS = 90 * 1000;
const GROK_VERIFICATION_PAGE_STATE = 'verification_code_entry';
const GROK_VERIFICATION_READY_TIMEOUT_MS = 90 * 1000;
const GROK_POST_PROFILE_CF_WAIT_MS = 20 * 1000; const GROK_POST_PROFILE_CF_WAIT_MS = 20 * 1000;
const GROK_PRE_SSO_EXTRACT_WAIT_MS = 10 * 1000; const GROK_PRE_SSO_EXTRACT_WAIT_MS = 10 * 1000;
const MAIL_2925_FILTER_LOOKBACK_MS = 10 * 60 * 1000; const MAIL_2925_FILTER_LOOKBACK_MS = 10 * 60 * 1000;
@@ -191,6 +193,50 @@
return result || {}; return result || {};
} }
async function getGrokRegisterPageState(options = {}) {
return sendGrokCommand('GET_PAGE_STATE', {}, {
step: options.step || 0,
timeoutMs: options.timeoutMs || 15000,
logMessage: options.logMessage || '',
});
}
async function waitForGrokVerificationPageReady(tabId, options = {}) {
const timeoutMs = Math.max(1000, Number(options.timeoutMs) || GROK_VERIFICATION_READY_TIMEOUT_MS);
const intervalMs = Math.max(250, Number(options.intervalMs) || 1000);
const deadline = Date.now() + timeoutMs;
let lastState = null;
let lastError = '';
while (Date.now() <= deadline) {
throwIfStopped();
try {
await ensureContentReady(tabId, {
timeoutMs: Math.min(DEFAULT_GROK_PAGE_TIMEOUT_MS, Math.max(5000, intervalMs + 3000)),
stableMs: 500,
initialDelayMs: 0,
logMessage: options.logMessage || '',
});
lastState = await getGrokRegisterPageState({
step: options.step || 0,
timeoutMs: Math.max(5000, intervalMs + 3000),
});
lastError = '';
if (lastState?.state === GROK_VERIFICATION_PAGE_STATE) {
return lastState;
}
} catch (error) {
lastError = getErrorMessage(error);
}
await sleepWithStop(Math.min(intervalMs, Math.max(0, deadline - Date.now())));
}
const stateLabel = cleanString(lastState?.state) || 'unknown';
const urlLabel = cleanString(lastState?.url);
const errorLabel = lastError ? `,最后通信错误:${lastError}` : '';
throw new Error(`Grok 邮箱提交后尚未进入验证码页面,当前状态:${stateLabel}${urlLabel ? `URL${urlLabel}` : ''}${errorLabel}`);
}
function shouldClearGrokCookie(cookie = {}) { function shouldClearGrokCookie(cookie = {}) {
const domain = cleanString(cookie.domain).replace(/^\.+/, '').toLowerCase(); const domain = cleanString(cookie.domain).replace(/^\.+/, '').toLowerCase();
return GROK_COOKIE_CLEAR_DOMAINS.some((target) => ( return GROK_COOKIE_CLEAR_DOMAINS.some((target) => (
@@ -395,8 +441,12 @@
}); });
const result = await sendGrokCommand(nodeId, { email }, { const result = await sendGrokCommand(nodeId, { email }, {
step: 2, step: 2,
timeoutMs: GROK_VERIFICATION_READY_TIMEOUT_MS + 15000,
logMessage: '步骤 2:正在提交 Grok 注册邮箱...', logMessage: '步骤 2:正在提交 Grok 注册邮箱...',
}); });
if (result.state !== GROK_VERIFICATION_PAGE_STATE) {
throw new Error(`Grok 邮箱提交后尚未进入验证码页面,当前状态:${cleanString(result.state) || 'unknown'}${cleanString(result.url) ? `URL${cleanString(result.url)}` : ''}`);
}
await log(`步骤 2:已提交 Grok 注册邮箱 ${email}`, 'ok', nodeId); await log(`步骤 2:已提交 Grok 注册邮箱 ${email}`, 'ok', nodeId);
await completeNode(nodeId, { await completeNode(nodeId, {
grokEmail: email, grokEmail: email,
@@ -456,6 +506,23 @@
|| currentState.runtimeState?.flowState?.grok?.register?.email || currentState.runtimeState?.flowState?.grok?.register?.email
|| currentState.email || currentState.email
).toLowerCase(); ).toLowerCase();
const tabId = await ensureGrokRegisterTab(currentState, { openIfMissing: false });
await activateTab(tabId);
const readyState = await waitForGrokVerificationPageReady(tabId, {
step: 3,
logMessage: '步骤 3:正在等待 Grok 验证码页面就绪...',
});
await persistState({
grokPageState: readyState.state || '',
grokPageUrl: readyState.url || '',
...buildGrokRuntimePatch({
session: {
pageState: readyState.state || '',
pageUrl: readyState.url || '',
lastError: '',
},
}),
});
const pollResult = await pollFlowVerificationCode({ const pollResult = await pollFlowVerificationCode({
actionLabel: 'Grok 验证码', actionLabel: 'Grok 验证码',
filterAfterTimestamp, filterAfterTimestamp,
@@ -478,7 +545,6 @@
if (!code) { if (!code) {
throw new Error('未能获取到 xAI 邮箱验证码。'); throw new Error('未能获取到 xAI 邮箱验证码。');
} }
const tabId = await ensureGrokRegisterTab(currentState, { openIfMissing: false });
await activateTab(tabId); await activateTab(tabId);
await ensureContentReady(tabId); await ensureContentReady(tabId);
const result = await sendGrokCommand(nodeId, { code }, { const result = await sendGrokCommand(nodeId, { code }, {
+26 -1
View File
@@ -5,6 +5,7 @@ const GROK_SIGNUP_URL = 'https://accounts.x.ai/sign-up?redirect=grok-com';
const GROK_EMAIL_SIGNUP_TEXT_PATTERN = /使用邮箱注册|sign\s*up\s*with\s*email|continue\s*with\s*email|email/i; const GROK_EMAIL_SIGNUP_TEXT_PATTERN = /使用邮箱注册|sign\s*up\s*with\s*email|continue\s*with\s*email|email/i;
const GROK_CONTINUE_TEXT_PATTERN = /continue|next|sign\s*up|submit|verify|继续|下一步|注册|提交|验证/i; const GROK_CONTINUE_TEXT_PATTERN = /continue|next|sign\s*up|submit|verify|继续|下一步|注册|提交|验证/i;
const GROK_PROFILE_TEXT_PATTERN = /given\s*name|family\s*name|first\s*name|last\s*name|password|名字|姓氏|密码/i; const GROK_PROFILE_TEXT_PATTERN = /given\s*name|family\s*name|first\s*name|last\s*name|password|名字|姓氏|密码/i;
const GROK_EMAIL_VERIFICATION_READY_TIMEOUT_MS = 90 * 1000;
const GROK_PROFILE_SUBMIT_PRE_CLICK_DELAY_MS = 2000; const GROK_PROFILE_SUBMIT_PRE_CLICK_DELAY_MS = 2000;
function isVisibleGrokElement(element) { function isVisibleGrokElement(element) {
@@ -174,6 +175,29 @@ function getGrokEmailErrorText() {
return ''; return '';
} }
async function waitForGrokVerificationPageAfterEmailSubmit() {
const settledState = await waitForGrok(() => {
const errorText = getGrokEmailErrorText();
if (errorText) return { state: 'email_error', error: errorText, url: location.href };
const state = getGrokPageState();
return state === 'verification_code_entry' ? { state, url: location.href } : null;
}, { timeoutMs: GROK_EMAIL_VERIFICATION_READY_TIMEOUT_MS, intervalMs: 500 });
if (settledState?.error) {
throw new Error(settledState.error);
}
if (settledState?.state === 'verification_code_entry') {
return settledState;
}
const errorText = getGrokEmailErrorText();
if (errorText) {
throw new Error(errorText);
}
const finalState = getGrokPageState();
throw new Error(`提交 Grok 注册邮箱后未进入验证码页面,当前页面状态:${finalState || 'unknown'}。请确认页面已跳转到“验证您的邮箱”后再继续。`);
}
async function submitGrokEmail(payload = {}) { async function submitGrokEmail(payload = {}) {
const email = String(payload.email || '').trim(); const email = String(payload.email || '').trim();
if (!email) throw new Error('缺少 Grok 注册邮箱。'); if (!email) throw new Error('缺少 Grok 注册邮箱。');
@@ -189,7 +213,8 @@ async function submitGrokEmail(payload = {}) {
if (errorText) { if (errorText) {
throw new Error(errorText); throw new Error(errorText);
} }
return { submitted: true, state: getGrokPageState(), url: location.href }; const readyState = await waitForGrokVerificationPageAfterEmailSubmit();
return { submitted: true, state: readyState.state, url: readyState.url || location.href };
} }
function getGrokVerificationErrorText() { function getGrokVerificationErrorText() {
+1 -1
View File
@@ -35,7 +35,7 @@
stepDefinitionMode: 'grok', stepDefinitionMode: 'grok',
targetSelectorLabel: '来源', targetSelectorLabel: '来源',
}, },
baseGroups: ['grok-runtime-status'], baseGroups: ['grok-runtime-status', 'shared-auto-run', 'shared-settings-actions'],
targets: { targets: {
webchat2api: { webchat2api: {
id: 'webchat2api', id: 'webchat2api',
+7 -1
View File
@@ -40,12 +40,18 @@
"targetSelectorLabel": "来源" "targetSelectorLabel": "来源"
}, },
"baseGroups": [ "baseGroups": [
"kiro-runtime-status" "kiro-runtime-status",
"shared-auto-run",
"shared-settings-actions"
], ],
"targets": { "targets": {
"kiro-rs": { "kiro-rs": {
"id": "kiro-rs", "id": "kiro-rs",
"label": "kiro.rs", "label": "kiro.rs",
"defaultState": {
"baseUrl": "",
"apiKey": ""
},
"groups": [ "groups": [
"kiro-target-kiro-rs" "kiro-target-kiro-rs"
] ]
+48 -2
View File
@@ -46,13 +46,20 @@
"baseGroups": [ "baseGroups": [
"openai-plus", "openai-plus",
"openai-phone", "openai-phone",
"shared-auto-run",
"openai-oauth", "openai-oauth",
"openai-step6" "openai-step6",
"shared-settings-actions"
], ],
"targets": { "targets": {
"cpa": { "cpa": {
"id": "cpa", "id": "cpa",
"label": "CPA 面板", "label": "CPA 面板",
"defaultState": {
"vpsUrl": "",
"vpsPassword": "",
"localCpaStep9Mode": "submit"
},
"groups": [ "groups": [
"openai-target-cpa" "openai-target-cpa"
] ]
@@ -60,6 +67,18 @@
"sub2api": { "sub2api": {
"id": "sub2api", "id": "sub2api",
"label": "SUB2API", "label": "SUB2API",
"defaultState": {
"sub2apiUrl": "",
"sub2apiEmail": "",
"sub2apiPassword": "",
"sub2apiGroupName": "codex",
"sub2apiGroupNames": [
"codex",
"openai-plus"
],
"sub2apiAccountPriority": 1,
"sub2apiDefaultProxyName": ""
},
"groups": [ "groups": [
"openai-target-sub2api" "openai-target-sub2api"
] ]
@@ -67,11 +86,37 @@
"codex2api": { "codex2api": {
"id": "codex2api", "id": "codex2api",
"label": "Codex2API", "label": "Codex2API",
"defaultState": {
"codex2apiUrl": "",
"codex2apiAdminKey": ""
},
"groups": [ "groups": [
"openai-target-codex2api" "openai-target-codex2api"
] ]
} }
}, },
"settingsDefaults": {
"signup": {
"signupMethod": "email",
"phoneVerificationEnabled": false,
"phoneSignupReloginAfterBindEmailEnabled": false
},
"plus": {
"plusModeEnabled": false,
"plusPaymentMethod": "paypal-hosted",
"plusAccountAccessStrategy": "oauth",
"hostedCheckoutVerificationUrl": "",
"hostedCheckoutPhoneNumber": "",
"plusHostedCheckoutOauthDelaySeconds": 3
},
"autoRun": {
"stepExecutionRange": {
"enabled": false,
"fromStep": 1,
"toStep": 11
}
}
},
"runtimeSources": { "runtimeSources": {
"openai-auth": { "openai-auth": {
"flowId": "openai", "flowId": "openai",
@@ -383,7 +428,8 @@
"label": "OAuth", "label": "OAuth",
"rowIds": [ "rowIds": [
"row-oauth-flow-timeout", "row-oauth-flow-timeout",
"row-oauth-display" "row-oauth-display",
"row-oauth-callback"
] ]
}, },
"openai-step6": { "openai-step6": {
+1 -1
View File
@@ -1263,7 +1263,7 @@ function updateIpProxyUI(state = latestState) {
const isAccountMode = mode === 'account'; const isAccountMode = mode === 'account';
const showSessionOptions = isAccountMode && service === '711proxy'; const showSessionOptions = isAccountMode && service === '711proxy';
const hasAccountListConfigured = accountListAvailable && isAccountMode && hasCurrentInputAccountListEntries(); const hasAccountListConfigured = accountListAvailable && isAccountMode && hasCurrentInputAccountListEntries();
const canOperate = !isAutoRunLockedPhase() && !isAutoRunScheduledPhase(); const canOperate = !isAutoRunLockedPhase();
const actionState = getIpProxyActionState(); const actionState = getIpProxyActionState();
const actionBusy = Boolean(actionState.busy); const actionBusy = Boolean(actionState.busy);
const busyAction = normalizeIpProxyActionType(actionState.action); const busyAction = normalizeIpProxyActionType(actionState.action);
+19 -23
View File
@@ -1268,6 +1268,19 @@ header {
width: 76px; width: 76px;
} }
#row-flow-selector .data-label,
#row-source-selector .data-label,
#row-grok-webchat2api-url .data-label,
#row-grok-webchat2api-key .data-label,
#row-grok-register-status .data-label,
#row-grok-sso-status .data-label,
#row-grok-webchat2api-upload-status .data-label,
#row-grok-sso-settings .data-label {
width: 76px;
text-transform: none;
letter-spacing: 0.02em;
}
#row-codex2api-url .data-label { #row-codex2api-url .data-label {
font-size: 10px; font-size: 10px;
letter-spacing: 0.02em; letter-spacing: 0.02em;
@@ -2197,15 +2210,6 @@ header {
justify-content: flex-end; justify-content: flex-end;
} }
.auto-delay-setting-pair {
flex-wrap: nowrap;
}
#row-auto-delay-settings .setting-group-secondary {
margin-left: auto;
min-width: 0;
}
.step6-cookie-cleanup-setting { .step6-cookie-cleanup-setting {
gap: 8px; gap: 8px;
} }
@@ -2227,10 +2231,6 @@ header {
text-align: center; text-align: center;
} }
.auto-run-delay-setting {
margin-left: auto !important;
}
.oauth-flow-timeout-setting { .oauth-flow-timeout-setting {
gap: 8px; gap: 8px;
min-width: 0; min-width: 0;
@@ -2242,10 +2242,6 @@ header {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#row-auto-delay-settings .setting-caption {
min-width: auto;
}
.setting-caption-left { .setting-caption-left {
text-align: left; text-align: left;
} }
@@ -2720,7 +2716,7 @@ header {
.auto-continue-bar svg { color: var(--orange); flex-shrink: 0; } .auto-continue-bar svg { color: var(--orange); flex-shrink: 0; }
.auto-hint { font-size: 13px; color: var(--orange); flex: 1; font-weight: 500; } .auto-hint { font-size: 13px; color: var(--orange); flex: 1; font-weight: 500; }
.auto-schedule-bar { .auto-countdown-bar {
margin-top: 8px; margin-top: 8px;
padding: 10px; padding: 10px;
background: var(--blue-soft); background: var(--blue-soft);
@@ -2732,12 +2728,12 @@ header {
gap: 10px; gap: 10px;
} }
.auto-schedule-bar svg { .auto-countdown-bar svg {
color: var(--blue); color: var(--blue);
flex-shrink: 0; flex-shrink: 0;
} }
.auto-schedule-copy { .auto-countdown-copy {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 2px; gap: 2px;
@@ -2745,19 +2741,19 @@ header {
min-width: 0; min-width: 0;
} }
.auto-schedule-title { .auto-countdown-title {
font-size: 13px; font-size: 13px;
font-weight: 700; font-weight: 700;
color: var(--blue); color: var(--blue);
} }
.auto-schedule-meta { .auto-countdown-meta {
font-size: 12px; font-size: 12px;
line-height: 1.5; line-height: 1.5;
color: var(--text-secondary); color: var(--text-secondary);
} }
.auto-schedule-actions { .auto-countdown-actions {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
+40 -49
View File
@@ -121,7 +121,7 @@
<section id="data-section"> <section id="data-section">
<div id="settings-card" class="data-card"> <div id="settings-card" class="data-card">
<div class="data-row"> <div class="data-row" id="row-flow-selector">
<span class="data-label">注册</span> <span class="data-label">注册</span>
<select id="select-flow" class="data-select" aria-label="Flow 选择"> <select id="select-flow" class="data-select" aria-label="Flow 选择">
<option value="openai" selected>Codex / OpenAI</option> <option value="openai" selected>Codex / OpenAI</option>
@@ -129,13 +129,16 @@
<option value="grok">Grok</option> <option value="grok">Grok</option>
</select> </select>
</div> </div>
<div class="data-row"> <div class="data-row" id="row-source-selector">
<span id="label-source-selector" class="data-label">来源</span> <span id="label-source-selector" class="data-label">来源</span>
<div class="data-inline">
<select id="select-panel-mode" class="data-select"> <select id="select-panel-mode" class="data-select">
<option value="cpa">CPA 面板</option> <option value="cpa">CPA 面板</option>
<option value="sub2api">SUB2API</option> <option value="sub2api">SUB2API</option>
<option value="codex2api">Codex2API</option> <option value="codex2api">Codex2API</option>
</select> </select>
<button id="btn-open-webchat2api-github" class="btn btn-ghost btn-xs data-inline-btn" type="button">GitHub</button>
</div>
</div> </div>
<div id="contribution-mode-panel" class="contribution-mode-panel" hidden> <div id="contribution-mode-panel" class="contribution-mode-panel" hidden>
<div class="contribution-mode-panel-header"> <div class="contribution-mode-panel-header">
@@ -291,20 +294,8 @@
<span class="data-label">上传状态</span> <span class="data-label">上传状态</span>
<span id="display-kiro-upload-status" class="data-value mono">未开始</span> <span id="display-kiro-upload-status" class="data-value mono">未开始</span>
</div> </div>
<div class="data-row" id="row-grok-register-status" style="display:none;">
<span class="data-label">Grok 注册</span>
<span id="display-grok-register-status" class="data-value mono">未开始</span>
</div>
<div class="data-row" id="row-grok-sso-status" style="display:none;">
<span class="data-label">SSO 状态</span>
<span id="display-grok-sso-status" class="data-value mono">未提取</span>
</div>
<div class="data-row" id="row-grok-webchat2api-upload-status" style="display:none;">
<span class="data-label">上传状态</span>
<span id="display-grok-webchat2api-upload-status" class="data-value mono">未开始</span>
</div>
<div class="data-row" id="row-grok-webchat2api-url" style="display:none;"> <div class="data-row" id="row-grok-webchat2api-url" style="display:none;">
<span class="data-label">webchat2api</span> <span class="data-label">webchat</span>
<input type="text" id="input-grok-webchat2api-url" class="data-input" <input type="text" id="input-grok-webchat2api-url" class="data-input"
placeholder="请输入 webchat2api 地址,例如 http://localhost:83" /> placeholder="请输入 webchat2api 地址,例如 http://localhost:83" />
</div> </div>
@@ -319,6 +310,18 @@
title="显示 webchat2api 管理密钥"></button> title="显示 webchat2api 管理密钥"></button>
</div> </div>
</div> </div>
<div class="data-row" id="row-grok-register-status" style="display:none;">
<span class="data-label">Grok 注册</span>
<span id="display-grok-register-status" class="data-value mono">未开始</span>
</div>
<div class="data-row" id="row-grok-sso-status" style="display:none;">
<span class="data-label">SSO 状态</span>
<span id="display-grok-sso-status" class="data-value mono">未提取</span>
</div>
<div class="data-row" id="row-grok-webchat2api-upload-status" style="display:none;">
<span class="data-label">上传状态</span>
<span id="display-grok-webchat2api-upload-status" class="data-value mono">未开始</span>
</div>
<div class="data-row" id="row-grok-sso-settings" style="display:none;"> <div class="data-row" id="row-grok-sso-settings" style="display:none;">
<span class="data-label">SSO Cookie</span> <span class="data-label">SSO Cookie</span>
<div class="data-inline"> <div class="data-inline">
@@ -694,26 +697,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="data-row module-divider-start" id="row-auto-delay-settings"> <div class="data-row module-divider-start" id="row-shared-auto-run">
<span class="data-label">启动前</span>
<div class="data-inline setting-pair auto-delay-setting-pair">
<div class="setting-group setting-group-secondary auto-run-delay-setting">
<span class="setting-caption">延迟</span>
<label class="toggle-switch" for="input-auto-delay-enabled">
<input type="checkbox" id="input-auto-delay-enabled" />
<span class="toggle-switch-track" aria-hidden="true">
<span class="toggle-switch-thumb"></span>
</span>
</label>
<div class="setting-controls">
<input type="number" id="input-auto-delay-minutes" class="data-input auto-delay-input" value="30" min="1"
max="1440" step="1" title="启动前倒计时分钟数" />
<span class="data-unit">分钟</span>
</div>
</div>
</div>
</div>
<div class="data-row">
<span class="data-label">自动重试</span> <span class="data-label">自动重试</span>
<div class="data-inline setting-pair"> <div class="data-inline setting-pair">
<div class="setting-group setting-group-primary"> <div class="setting-group setting-group-primary">
@@ -734,6 +718,16 @@
</div> </div>
</div> </div>
</div> </div>
<div class="data-row" id="row-auto-run-thread-interval">
<span class="data-label">线程间隔</span>
<div class="data-inline">
<div class="setting-controls">
<input type="number" id="input-auto-skip-failures-thread-interval-minutes"
class="data-input auto-delay-input" value="0" min="0" max="1440" step="1" title="兜底模式下,两轮线程之间的等待分钟数" />
<span class="data-unit">分钟</span>
</div>
</div>
</div>
<div class="data-row" id="row-oauth-flow-timeout"> <div class="data-row" id="row-oauth-flow-timeout">
<span class="data-label">授权总超时</span> <span class="data-label">授权总超时</span>
<div class="data-inline setting-pair"> <div class="data-inline setting-pair">
@@ -748,14 +742,6 @@
</label> </label>
<span class="setting-caption oauth-flow-timeout-caption">关闭后只取消 Step 7 后链总预算</span> <span class="setting-caption oauth-flow-timeout-caption">关闭后只取消 Step 7 后链总预算</span>
</div> </div>
<div class="setting-group setting-group-secondary">
<span class="setting-caption">线程间隔</span>
<div class="setting-controls">
<input type="number" id="input-auto-skip-failures-thread-interval-minutes"
class="data-input auto-delay-input" value="0" min="0" max="1440" step="1" title="兜底模式下,两轮线程之间的等待分钟数" />
<span class="data-unit">分钟</span>
</div>
</div>
</div> </div>
</div> </div>
<div class="data-row" id="row-step-execution-range"> <div class="data-row" id="row-step-execution-range">
@@ -782,10 +768,15 @@
<span class="data-label">OAuth</span> <span class="data-label">OAuth</span>
<span id="display-oauth-url" class="data-value mono truncate">等待中...</span> <span id="display-oauth-url" class="data-value mono truncate">等待中...</span>
</div> </div>
<div class="data-row"> <div class="data-row" id="row-oauth-callback">
<span class="data-label">回调</span> <span class="data-label">回调</span>
<div class="data-inline data-value-actions"> <div class="data-inline data-value-actions">
<span id="display-localhost-url" class="data-value data-value-fill mono truncate">等待中...</span> <span id="display-localhost-url" class="data-value data-value-fill mono truncate">等待中...</span>
</div>
</div>
<div class="data-row module-divider-start" id="row-settings-actions">
<span class="data-label">设置</span>
<div class="data-inline data-value-actions">
<button id="btn-save-settings" class="btn btn-outline btn-sm data-inline-btn" type="button">保存</button> <button id="btn-save-settings" class="btn btn-outline btn-sm data-inline-btn" type="button">保存</button>
</div> </div>
</div> </div>
@@ -1798,16 +1789,16 @@
<span class="auto-hint">先自动获取邮箱,或手动粘贴邮箱后再继续</span> <span class="auto-hint">先自动获取邮箱,或手动粘贴邮箱后再继续</span>
<button id="btn-auto-continue" class="btn btn-primary btn-sm">继续</button> <button id="btn-auto-continue" class="btn btn-primary btn-sm">继续</button>
</div> </div>
<div id="auto-schedule-bar" class="auto-schedule-bar" style="display:none;"> <div id="auto-countdown-bar" class="auto-countdown-bar" style="display:none;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" /> <circle cx="12" cy="12" r="10" />
<polyline points="12 6 12 12 16 14" /> <polyline points="12 6 12 12 16 14" />
</svg> </svg>
<div class="auto-schedule-copy"> <div class="auto-countdown-copy">
<span id="auto-schedule-title" class="auto-schedule-title">已计划自动运行</span> <span id="auto-countdown-title" class="auto-countdown-title">等待继续</span>
<span id="auto-schedule-meta" class="auto-schedule-meta">等待倒计时开始...</span> <span id="auto-countdown-meta" class="auto-countdown-meta">等待倒计时开始...</span>
</div> </div>
<div class="auto-schedule-actions"> <div class="auto-countdown-actions">
<button id="btn-auto-run-now" class="btn btn-primary btn-sm" type="button">立即开始</button> <button id="btn-auto-run-now" class="btn btn-primary btn-sm" type="button">立即开始</button>
<button id="btn-auto-cancel-schedule" class="btn btn-outline btn-sm" type="button">取消</button> <button id="btn-auto-cancel-schedule" class="btn btn-outline btn-sm" type="button">取消</button>
</div> </div>
+50 -159
View File
@@ -77,11 +77,10 @@ const stepsProgress = document.getElementById('steps-progress');
const btnAutoRun = document.getElementById('btn-auto-run'); const btnAutoRun = document.getElementById('btn-auto-run');
const btnAutoContinue = document.getElementById('btn-auto-continue'); const btnAutoContinue = document.getElementById('btn-auto-continue');
const autoContinueBar = document.getElementById('auto-continue-bar'); const autoContinueBar = document.getElementById('auto-continue-bar');
const autoScheduleBar = document.getElementById('auto-schedule-bar'); const autoCountdownBar = document.getElementById('auto-countdown-bar');
const autoScheduleTitle = document.getElementById('auto-schedule-title'); const autoCountdownTitle = document.getElementById('auto-countdown-title');
const autoScheduleMeta = document.getElementById('auto-schedule-meta'); const autoCountdownMeta = document.getElementById('auto-countdown-meta');
const btnAutoRunNow = document.getElementById('btn-auto-run-now'); const btnAutoRunNow = document.getElementById('btn-auto-run-now');
const btnAutoCancelSchedule = document.getElementById('btn-auto-cancel-schedule');
const btnClearLog = document.getElementById('btn-clear-log'); const btnClearLog = document.getElementById('btn-clear-log');
const configMenuShell = document.getElementById('config-menu-shell'); const configMenuShell = document.getElementById('config-menu-shell');
const btnConfigMenu = document.getElementById('btn-config-menu'); const btnConfigMenu = document.getElementById('btn-config-menu');
@@ -91,6 +90,7 @@ const btnImportSettings = document.getElementById('btn-import-settings');
const inputImportSettingsFile = document.getElementById('input-import-settings-file'); const inputImportSettingsFile = document.getElementById('input-import-settings-file');
const labelSourceSelector = document.getElementById('label-source-selector'); const labelSourceSelector = document.getElementById('label-source-selector');
const selectPanelMode = document.getElementById('select-panel-mode'); const selectPanelMode = document.getElementById('select-panel-mode');
const btnOpenWebchat2ApiGithub = document.getElementById('btn-open-webchat2api-github');
const rowVpsUrl = document.getElementById('row-vps-url'); const rowVpsUrl = document.getElementById('row-vps-url');
const inputVpsUrl = document.getElementById('input-vps-url'); const inputVpsUrl = document.getElementById('input-vps-url');
const rowVpsPassword = document.getElementById('row-vps-password'); const rowVpsPassword = document.getElementById('row-vps-password');
@@ -428,8 +428,6 @@ const inputRunCount = document.getElementById('input-run-count');
const inputAutoSkipFailures = document.getElementById('input-auto-skip-failures'); const inputAutoSkipFailures = document.getElementById('input-auto-skip-failures');
const inputAutoSkipFailuresThreadIntervalMinutes = document.getElementById('input-auto-skip-failures-thread-interval-minutes'); const inputAutoSkipFailuresThreadIntervalMinutes = document.getElementById('input-auto-skip-failures-thread-interval-minutes');
const inputStep6CookieCleanupEnabled = document.getElementById('input-step6-cookie-cleanup-enabled'); const inputStep6CookieCleanupEnabled = document.getElementById('input-step6-cookie-cleanup-enabled');
const inputAutoDelayEnabled = document.getElementById('input-auto-delay-enabled');
const inputAutoDelayMinutes = document.getElementById('input-auto-delay-minutes');
const inputAutoStepDelaySeconds = document.getElementById('input-auto-step-delay-seconds'); const inputAutoStepDelaySeconds = document.getElementById('input-auto-step-delay-seconds');
const inputOAuthFlowTimeoutEnabled = document.getElementById('input-oauth-flow-timeout-enabled'); const inputOAuthFlowTimeoutEnabled = document.getElementById('input-oauth-flow-timeout-enabled');
const rowStepExecutionRange = document.getElementById('row-step-execution-range'); const rowStepExecutionRange = document.getElementById('row-step-execution-range');
@@ -628,9 +626,6 @@ let SKIPPABLE_STEPS = new Set(STEP_IDS);
let NODE_IDS = workflowNodes.map((node) => String(node.nodeId || '').trim()).filter(Boolean); let NODE_IDS = workflowNodes.map((node) => String(node.nodeId || '').trim()).filter(Boolean);
let NODE_DEFAULT_STATUSES = Object.fromEntries(NODE_IDS.map((nodeId) => [nodeId, 'pending'])); let NODE_DEFAULT_STATUSES = Object.fromEntries(NODE_IDS.map((nodeId) => [nodeId, 'pending']));
let SKIPPABLE_NODES = new Set(NODE_IDS); let SKIPPABLE_NODES = new Set(NODE_IDS);
const AUTO_DELAY_MIN_MINUTES = 1;
const AUTO_DELAY_MAX_MINUTES = 1440;
const AUTO_DELAY_DEFAULT_MINUTES = 30;
const AUTO_FALLBACK_THREAD_INTERVAL_MIN_MINUTES = 0; const AUTO_FALLBACK_THREAD_INTERVAL_MIN_MINUTES = 0;
const AUTO_FALLBACK_THREAD_INTERVAL_MAX_MINUTES = 1440; const AUTO_FALLBACK_THREAD_INTERVAL_MAX_MINUTES = 1440;
const AUTO_FALLBACK_THREAD_INTERVAL_DEFAULT_MINUTES = 0; const AUTO_FALLBACK_THREAD_INTERVAL_DEFAULT_MINUTES = 0;
@@ -1365,7 +1360,6 @@ let currentAutoRun = {
currentRun: 0, currentRun: 0,
totalRuns: 1, totalRuns: 1,
attemptRun: 0, attemptRun: 0,
scheduledAt: null,
countdownAt: null, countdownAt: null,
countdownTitle: '', countdownTitle: '',
countdownNote: '', countdownNote: '',
@@ -1386,7 +1380,7 @@ let currentModalActions = [];
let modalResultBuilder = null; let modalResultBuilder = null;
let activePlusManualConfirmationRequestId = ''; let activePlusManualConfirmationRequestId = '';
let plusManualConfirmationDialogInFlight = false; let plusManualConfirmationDialogInFlight = false;
let scheduledCountdownTimer = null; let autoRunCountdownTimer = null;
let configMenuOpen = false; let configMenuOpen = false;
let configActionInFlight = false; let configActionInFlight = false;
let currentReleaseSnapshot = null; let currentReleaseSnapshot = null;
@@ -1426,9 +1420,7 @@ function shouldAttachAutomationWindow(message = {}) {
return [ return [
'EXECUTE_NODE', 'EXECUTE_NODE',
'AUTO_RUN', 'AUTO_RUN',
'SCHEDULE_AUTO_RUN',
'RESUME_AUTO_RUN', 'RESUME_AUTO_RUN',
'START_SCHEDULED_AUTO_RUN_NOW',
'SKIP_AUTO_RUN_COUNTDOWN', 'SKIP_AUTO_RUN_COUNTDOWN',
'PROBE_IP_PROXY_EXIT', 'PROBE_IP_PROXY_EXIT',
].includes(String(message?.type || '').trim()); ].includes(String(message?.type || '').trim());
@@ -1680,7 +1672,6 @@ const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = Object.fromEntries(
const getIcloudLoginUrlForHost = window.IcloudUtils?.getIcloudLoginUrlForHost const getIcloudLoginUrlForHost = window.IcloudUtils?.getIcloudLoginUrlForHost
|| ((host) => host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : (host === 'icloud.com' ? 'https://www.icloud.com/' : '')); || ((host) => host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : (host === 'icloud.com' ? 'https://www.icloud.com/' : ''));
btnAutoCancelSchedule?.remove();
const MAIL_PROVIDER_LOGIN_CONFIGS = { const MAIL_PROVIDER_LOGIN_CONFIGS = {
[ICLOUD_PROVIDER]: { [ICLOUD_PROVIDER]: {
label: 'iCloud 邮箱', label: 'iCloud 邮箱',
@@ -2982,7 +2973,7 @@ function applyStepExecutionRangeState(state = latestState) {
if (inputStepExecutionRangeEnabled) { if (inputStepExecutionRangeEnabled) {
inputStepExecutionRangeEnabled.checked = Boolean(range.enabled); inputStepExecutionRangeEnabled.checked = Boolean(range.enabled);
} }
const controlsDisabled = !available || isAutoRunLockedPhase() || isAutoRunScheduledPhase(); const controlsDisabled = !available || isAutoRunLockedPhase();
if (inputStepExecutionRangeEnabled) inputStepExecutionRangeEnabled.disabled = controlsDisabled; if (inputStepExecutionRangeEnabled) inputStepExecutionRangeEnabled.disabled = controlsDisabled;
if (inputStepExecutionRangeFrom) inputStepExecutionRangeFrom.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked; if (inputStepExecutionRangeFrom) inputStepExecutionRangeFrom.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked;
if (inputStepExecutionRangeTo) inputStepExecutionRangeTo.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked; if (inputStepExecutionRangeTo) inputStepExecutionRangeTo.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked;
@@ -3083,7 +3074,7 @@ function hasSavedProgress(state = latestState) {
function isContributionModeSwitchBlocked(state = latestState) { function isContributionModeSwitchBlocked(state = latestState) {
const statuses = getStepStatuses(state); const statuses = getStepStatuses(state);
const anyRunning = Object.values(statuses).some((status) => status === 'running'); const anyRunning = Object.values(statuses).some((status) => status === 'running');
return anyRunning || isAutoRunLockedPhase() || isAutoRunPausedPhase() || isAutoRunScheduledPhase(); return anyRunning || isAutoRunLockedPhase() || isAutoRunPausedPhase();
} }
function shouldOfferAutoModeChoice(state = latestState) { function shouldOfferAutoModeChoice(state = latestState) {
@@ -3630,7 +3621,7 @@ function syncAutoRunState(source = {}) {
const autoRunning = source.autoRunning !== undefined const autoRunning = source.autoRunning !== undefined
? Boolean(source.autoRunning) ? Boolean(source.autoRunning)
: (source.autoRunPhase !== undefined || source.phase !== undefined : (source.autoRunPhase !== undefined || source.phase !== undefined
? ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase) ? ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase)
: currentAutoRun.autoRunning); : currentAutoRun.autoRunning);
currentAutoRun = { currentAutoRun = {
@@ -3639,7 +3630,6 @@ function syncAutoRunState(source = {}) {
currentRun: readAutoRunStateValue(source, ['autoRunCurrentRun', 'currentRun'], currentAutoRun.currentRun), currentRun: readAutoRunStateValue(source, ['autoRunCurrentRun', 'currentRun'], currentAutoRun.currentRun),
totalRuns: readAutoRunStateValue(source, ['autoRunTotalRuns', 'totalRuns'], currentAutoRun.totalRuns), totalRuns: readAutoRunStateValue(source, ['autoRunTotalRuns', 'totalRuns'], currentAutoRun.totalRuns),
attemptRun: readAutoRunStateValue(source, ['autoRunAttemptRun', 'attemptRun'], currentAutoRun.attemptRun), attemptRun: readAutoRunStateValue(source, ['autoRunAttemptRun', 'attemptRun'], currentAutoRun.attemptRun),
scheduledAt: readAutoRunStateValue(source, ['scheduledAutoRunAt', 'scheduledAt'], currentAutoRun.scheduledAt),
countdownAt: readAutoRunStateValue(source, ['autoRunCountdownAt', 'countdownAt'], currentAutoRun.countdownAt), countdownAt: readAutoRunStateValue(source, ['autoRunCountdownAt', 'countdownAt'], currentAutoRun.countdownAt),
countdownTitle: readAutoRunStateValue(source, ['autoRunCountdownTitle', 'countdownTitle'], currentAutoRun.countdownTitle), countdownTitle: readAutoRunStateValue(source, ['autoRunCountdownTitle', 'countdownTitle'], currentAutoRun.countdownTitle),
countdownNote: readAutoRunStateValue(source, ['autoRunCountdownNote', 'countdownNote'], currentAutoRun.countdownNote), countdownNote: readAutoRunStateValue(source, ['autoRunCountdownNote', 'countdownNote'], currentAutoRun.countdownNote),
@@ -3649,8 +3639,7 @@ function syncAutoRunState(source = {}) {
function isContributionButtonLocked() { function isContributionButtonLocked() {
const autoActive = currentAutoRun.autoRunning const autoActive = currentAutoRun.autoRunning
|| isAutoRunLockedPhase() || isAutoRunLockedPhase()
|| isAutoRunPausedPhase() || isAutoRunPausedPhase();
|| isAutoRunScheduledPhase();
if (autoActive) { if (autoActive) {
return false; return false;
} }
@@ -3675,12 +3664,8 @@ function isAutoRunWaitingStepPhase() {
return currentAutoRun.phase === 'waiting_step'; return currentAutoRun.phase === 'waiting_step';
} }
function isAutoRunScheduledPhase() {
return currentAutoRun.phase === 'scheduled';
}
function isAutoRunSourceSyncPhase(phase) { function isAutoRunSourceSyncPhase(phase) {
return ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase); return ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase);
} }
function shouldSyncRunCountFromAutoRunSource(source = {}) { function shouldSyncRunCountFromAutoRunSource(source = {}) {
@@ -3717,14 +3702,6 @@ function getAutoRunLabel(payload = currentAutoRun) {
return attemptLabel ? ` (${attemptLabel.slice(3)})` : ''; return attemptLabel ? ` (${attemptLabel.slice(3)})` : '';
} }
function normalizeAutoDelayMinutes(value) {
const numeric = Number(value);
if (!Number.isFinite(numeric)) {
return AUTO_DELAY_DEFAULT_MINUTES;
}
return Math.min(AUTO_DELAY_MAX_MINUTES, Math.max(AUTO_DELAY_MIN_MINUTES, Math.floor(numeric)));
}
function normalizeAutoRunThreadIntervalMinutes(value) { function normalizeAutoRunThreadIntervalMinutes(value) {
const rawValue = String(value ?? '').trim(); const rawValue = String(value ?? '').trim();
if (!rawValue) { if (!rawValue) {
@@ -3948,12 +3925,6 @@ function updateFallbackThreadIntervalInputState() {
inputAutoSkipFailuresThreadIntervalMinutes.disabled = Boolean(inputAutoSkipFailures.disabled); inputAutoSkipFailuresThreadIntervalMinutes.disabled = Boolean(inputAutoSkipFailures.disabled);
} }
function updateAutoDelayInputState() {
const scheduled = isAutoRunScheduledPhase();
inputAutoDelayEnabled.disabled = scheduled;
inputAutoDelayMinutes.disabled = scheduled || !inputAutoDelayEnabled.checked;
}
function formatCountdown(remainingMs) { function formatCountdown(remainingMs) {
const totalSeconds = Math.max(0, Math.ceil(remainingMs / 1000)); const totalSeconds = Math.max(0, Math.ceil(remainingMs / 1000));
const hours = Math.floor(totalSeconds / 3600); const hours = Math.floor(totalSeconds / 3600);
@@ -3974,21 +3945,12 @@ function formatScheduleTime(timestamp) {
}); });
} }
function stopScheduledCountdownTicker() { function stopAutoRunCountdownTicker() {
clearInterval(scheduledCountdownTimer); clearInterval(autoRunCountdownTimer);
scheduledCountdownTimer = null; autoRunCountdownTimer = null;
} }
function getActiveAutoRunCountdown() { function getActiveAutoRunCountdown() {
if (isAutoRunScheduledPhase() && Number.isFinite(currentAutoRun.scheduledAt)) {
return {
at: currentAutoRun.scheduledAt,
title: '已计划自动运行',
note: `计划于 ${formatScheduleTime(currentAutoRun.scheduledAt)} 开始`,
tone: 'scheduled',
};
}
if (currentAutoRun.phase !== 'waiting_interval') { if (currentAutoRun.phase !== 'waiting_interval') {
return null; return null;
} }
@@ -4005,48 +3967,45 @@ function getActiveAutoRunCountdown() {
}; };
} }
function renderScheduledAutoRunInfo() { function renderAutoRunCountdownInfo() {
if (!autoScheduleBar) { if (!autoCountdownBar) {
return; return;
} }
const countdown = getActiveAutoRunCountdown(); const countdown = getActiveAutoRunCountdown();
if (!countdown) { if (!countdown) {
autoScheduleBar.style.display = 'none'; autoCountdownBar.style.display = 'none';
return; return;
} }
const remainingMs = countdown.at - Date.now(); const remainingMs = countdown.at - Date.now();
autoScheduleBar.style.display = 'flex'; autoCountdownBar.style.display = 'flex';
if (btnAutoRunNow) { if (btnAutoRunNow) {
btnAutoRunNow.hidden = false; btnAutoRunNow.hidden = false;
btnAutoRunNow.textContent = currentAutoRun.phase === 'waiting_interval' ? '立即继续' : '立即开始'; btnAutoRunNow.textContent = '立即继续';
} }
if (btnAutoCancelSchedule) { autoCountdownTitle.textContent = countdown.title;
btnAutoCancelSchedule.hidden = true; autoCountdownMeta.textContent = remainingMs > 0
}
autoScheduleTitle.textContent = countdown.title;
autoScheduleMeta.textContent = remainingMs > 0
? `${countdown.note ? `${countdown.note}` : ''}剩余 ${formatCountdown(remainingMs)}` ? `${countdown.note ? `${countdown.note}` : ''}剩余 ${formatCountdown(remainingMs)}`
: '倒计时即将结束,正在准备继续...'; : '倒计时即将结束,正在准备继续...';
return; return;
} }
function syncScheduledCountdownTicker() { function syncAutoRunCountdownTicker() {
renderScheduledAutoRunInfo(); renderAutoRunCountdownInfo();
if (getActiveAutoRunCountdown()) { if (getActiveAutoRunCountdown()) {
if (scheduledCountdownTimer) { if (autoRunCountdownTimer) {
return; return;
} }
scheduledCountdownTimer = setInterval(() => { autoRunCountdownTimer = setInterval(() => {
renderScheduledAutoRunInfo(); renderAutoRunCountdownInfo();
updateStatusDisplay(latestState); updateStatusDisplay(latestState);
}, 1000); }, 1000);
return; return;
} }
stopScheduledCountdownTicker(); stopAutoRunCountdownTicker();
return; return;
} }
@@ -5159,8 +5118,6 @@ function collectSettingsPayload() {
stepExecutionRangeByFlow: typeof buildStepExecutionRangeByFlowPayload === 'function' stepExecutionRangeByFlow: typeof buildStepExecutionRangeByFlowPayload === 'function'
? buildStepExecutionRangeByFlowPayload(latestState?.stepExecutionRangeByFlow) ? buildStepExecutionRangeByFlowPayload(latestState?.stepExecutionRangeByFlow)
: (latestState?.stepExecutionRangeByFlow || {}), : (latestState?.stepExecutionRangeByFlow || {}),
autoRunDelayEnabled: inputAutoDelayEnabled.checked,
autoRunDelayMinutes: normalizeAutoDelayMinutes(inputAutoDelayMinutes.value),
autoStepDelaySeconds: normalizeAutoStepDelaySeconds(inputAutoStepDelaySeconds.value), autoStepDelaySeconds: normalizeAutoStepDelaySeconds(inputAutoStepDelaySeconds.value),
oauthFlowTimeoutEnabled: typeof inputOAuthFlowTimeoutEnabled !== 'undefined' && inputOAuthFlowTimeoutEnabled oauthFlowTimeoutEnabled: typeof inputOAuthFlowTimeoutEnabled !== 'undefined' && inputOAuthFlowTimeoutEnabled
? Boolean(inputOAuthFlowTimeoutEnabled.checked) ? Boolean(inputOAuthFlowTimeoutEnabled.checked)
@@ -9337,7 +9294,7 @@ function canSelectPhoneSignupMethod() {
} }
function isSignupMethodSwitchLocked() { function isSignupMethodSwitchLocked() {
return isAutoRunLockedPhase() || isAutoRunPausedPhase() || isAutoRunScheduledPhase(); return isAutoRunLockedPhase() || isAutoRunPausedPhase();
} }
function updateSignupMethodUI(options = {}) { function updateSignupMethodUI(options = {}) {
@@ -9581,7 +9538,7 @@ function updatePhoneVerificationSettingsUI() {
} }
} }
phoneSignupReuseUiWasLocked = phoneSignupReuseLocked; phoneSignupReuseUiWasLocked = phoneSignupReuseLocked;
const settingsLocked = isAutoRunLockedPhase() || isAutoRunScheduledPhase(); const settingsLocked = isAutoRunLockedPhase();
if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) { if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) {
inputPhoneSignupReloginAfterBindEmail.disabled = settingsLocked || !showPhoneSignupReloginAfterBindEmail; inputPhoneSignupReloginAfterBindEmail.disabled = settingsLocked || !showPhoneSignupReloginAfterBindEmail;
} }
@@ -10744,8 +10701,7 @@ function applyAutoRunStatus(payload = currentAutoRun) {
const runLabel = getAutoRunLabel(currentAutoRun); const runLabel = getAutoRunLabel(currentAutoRun);
const locked = isAutoRunLockedPhase(); const locked = isAutoRunLockedPhase();
const paused = isAutoRunPausedPhase(); const paused = isAutoRunPausedPhase();
const scheduled = isAutoRunScheduledPhase(); const settingsCardLocked = locked;
const settingsCardLocked = scheduled || locked;
setSettingsCardLocked(settingsCardLocked); setSettingsCardLocked(settingsCardLocked);
setFreePhoneReuseControlsLocked(settingsCardLocked); setFreePhoneReuseControlsLocked(settingsCardLocked);
@@ -10766,14 +10722,14 @@ function applyAutoRunStatus(payload = currentAutoRun) {
if (typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority) { if (typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority) {
inputSub2ApiAccountPriority.disabled = locked; inputSub2ApiAccountPriority.disabled = locked;
} }
inputAutoSkipFailures.disabled = scheduled; inputAutoSkipFailures.disabled = locked;
const lockedRunCount = typeof getLockedRunCountFromEmailPool === 'function' const lockedRunCount = typeof getLockedRunCountFromEmailPool === 'function'
? getLockedRunCountFromEmailPool() ? getLockedRunCountFromEmailPool()
: 0; : 0;
const isSyncPhase = typeof isAutoRunSourceSyncPhase === 'function' const isSyncPhase = typeof isAutoRunSourceSyncPhase === 'function'
? isAutoRunSourceSyncPhase ? isAutoRunSourceSyncPhase
: (phase) => ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase); : (phase) => ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase);
const shouldSyncRunCount = typeof shouldSyncRunCountFromAutoRunSource === 'function' const shouldSyncRunCount = typeof shouldSyncRunCountFromAutoRunSource === 'function'
? shouldSyncRunCountFromAutoRunSource(currentAutoRun) ? shouldSyncRunCountFromAutoRunSource(currentAutoRun)
: (currentAutoRun.autoRunning || isSyncPhase(currentAutoRun.phase)); : (currentAutoRun.autoRunning || isSyncPhase(currentAutoRun.phase));
@@ -10784,10 +10740,6 @@ function applyAutoRunStatus(payload = currentAutoRun) {
} }
switch (currentAutoRun.phase) { switch (currentAutoRun.phase) {
case 'scheduled':
autoContinueBar.style.display = 'none';
btnAutoRun.innerHTML = `已计划${runLabel}`;
break;
case 'waiting_step': case 'waiting_step':
autoContinueBar.style.display = 'none'; autoContinueBar.style.display = 'none';
btnAutoRun.innerHTML = `等待中${runLabel}`; btnAutoRun.innerHTML = `等待中${runLabel}`;
@@ -10818,10 +10770,9 @@ function applyAutoRunStatus(payload = currentAutoRun) {
break; break;
} }
updateAutoDelayInputState();
updateFallbackThreadIntervalInputState(); updateFallbackThreadIntervalInputState();
syncScheduledCountdownTicker(); syncAutoRunCountdownTicker();
updateStopButtonState(scheduled || paused || locked || Object.values(getStepStatuses()).some(status => status === 'running')); updateStopButtonState(paused || locked || Object.values(getStepStatuses()).some(status => status === 'running'));
updateConfigMenuControls(); updateConfigMenuControls();
renderContributionMode(); renderContributionMode();
} }
@@ -11422,8 +11373,6 @@ function applySettingsState(state) {
if (typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled) { if (typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled) {
inputStep6CookieCleanupEnabled.checked = Boolean(state?.step6CookieCleanupEnabled); inputStep6CookieCleanupEnabled.checked = Boolean(state?.step6CookieCleanupEnabled);
} }
inputAutoDelayEnabled.checked = Boolean(state?.autoRunDelayEnabled);
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(state?.autoRunDelayMinutes));
inputAutoStepDelaySeconds.value = formatAutoStepDelayInputValue(state?.autoStepDelaySeconds); inputAutoStepDelaySeconds.value = formatAutoStepDelayInputValue(state?.autoStepDelaySeconds);
if (typeof inputOAuthFlowTimeoutEnabled !== 'undefined' && inputOAuthFlowTimeoutEnabled) { if (typeof inputOAuthFlowTimeoutEnabled !== 'undefined' && inputOAuthFlowTimeoutEnabled) {
inputOAuthFlowTimeoutEnabled.checked = state?.oauthFlowTimeoutEnabled !== undefined inputOAuthFlowTimeoutEnabled.checked = state?.oauthFlowTimeoutEnabled !== undefined
@@ -11598,7 +11547,7 @@ function applySettingsState(state) {
} }
const isSyncPhase = typeof isAutoRunSourceSyncPhase === 'function' const isSyncPhase = typeof isAutoRunSourceSyncPhase === 'function'
? isAutoRunSourceSyncPhase ? isAutoRunSourceSyncPhase
: (phase) => ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase); : (phase) => ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase);
const shouldSyncInitialRunCount = typeof shouldSyncRunCountFromAutoRunSource === 'function' const shouldSyncInitialRunCount = typeof shouldSyncRunCountFromAutoRunSource === 'function'
? shouldSyncRunCountFromAutoRunSource(state) ? shouldSyncRunCountFromAutoRunSource(state)
: (Boolean(state?.autoRunning) || isSyncPhase(state?.autoRunPhase ?? state?.phase)); : (Boolean(state?.autoRunning) || isSyncPhase(state?.autoRunPhase ?? state?.phase));
@@ -11608,7 +11557,6 @@ function applySettingsState(state) {
applyAutoRunStatus(state); applyAutoRunStatus(state);
markSettingsDirty(false); markSettingsDirty(false);
updateAutoDelayInputState();
updateFallbackThreadIntervalInputState(); updateFallbackThreadIntervalInputState();
updateAccountRunHistorySettingsUI(); updateAccountRunHistorySettingsUI();
updatePhoneVerificationSettingsUI(); updatePhoneVerificationSettingsUI();
@@ -13657,7 +13605,6 @@ function updateButtonStates() {
const statuses = getNodeStatuses(); const statuses = getNodeStatuses();
const anyRunning = Object.values(statuses).some(s => s === 'running'); const anyRunning = Object.values(statuses).some(s => s === 'running');
const autoLocked = isAutoRunLockedPhase(); const autoLocked = isAutoRunLockedPhase();
const autoScheduled = isAutoRunScheduledPhase();
const enabledNodeIds = getEnabledNodeIdsForStepExecutionRange(latestState); const enabledNodeIds = getEnabledNodeIdsForStepExecutionRange(latestState);
const icloudTargetMailboxTypeValue = typeof selectIcloudTargetMailboxType !== 'undefined' const icloudTargetMailboxTypeValue = typeof selectIcloudTargetMailboxType !== 'undefined'
? selectIcloudTargetMailboxType?.value ? selectIcloudTargetMailboxType?.value
@@ -13671,7 +13618,7 @@ function updateButtonStates() {
if (currentStatus === 'disabled') { if (currentStatus === 'disabled') {
btn.disabled = true; btn.disabled = true;
} else if (anyRunning || autoLocked || autoScheduled) { } else if (anyRunning || autoLocked) {
btn.disabled = true; btn.disabled = true;
} else if (enabledNodeIds.indexOf(nodeId) === 0) { } else if (enabledNodeIds.indexOf(nodeId) === 0) {
btn.disabled = false; btn.disabled = false;
@@ -13691,7 +13638,7 @@ function updateButtonStates() {
const prevNodeId = currentIndex > 0 ? enabledNodeIds[currentIndex - 1] : null; const prevNodeId = currentIndex > 0 ? enabledNodeIds[currentIndex - 1] : null;
const prevStatus = prevNodeId === null ? 'completed' : statuses[prevNodeId]; const prevStatus = prevNodeId === null ? 'completed' : statuses[prevNodeId];
if (!SKIPPABLE_NODES.has(nodeId) || currentStatus === 'disabled' || anyRunning || autoLocked || autoScheduled || currentStatus === 'running' || isDoneStatus(currentStatus)) { if (!SKIPPABLE_NODES.has(nodeId) || currentStatus === 'disabled' || anyRunning || autoLocked || currentStatus === 'running' || isDoneStatus(currentStatus)) {
btn.style.display = 'none'; btn.style.display = 'none';
btn.disabled = true; btn.disabled = true;
btn.title = '当前不可跳过'; btn.title = '当前不可跳过';
@@ -13710,8 +13657,8 @@ function updateButtonStates() {
btn.title = `跳过节点 ${nodeId}`; btn.title = `跳过节点 ${nodeId}`;
}); });
btnReset.disabled = anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked; btnReset.disabled = anyRunning || isAutoRunPausedPhase() || autoLocked;
const disableIcloudControls = anyRunning || autoScheduled || autoLocked; const disableIcloudControls = anyRunning || autoLocked;
if (btnIcloudRefresh) btnIcloudRefresh.disabled = disableIcloudControls; if (btnIcloudRefresh) btnIcloudRefresh.disabled = disableIcloudControls;
if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = disableIcloudControls || !hasDeletableUsedIcloudAliases(); if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = disableIcloudControls || !hasDeletableUsedIcloudAliases();
if (selectIcloudHostPreference) selectIcloudHostPreference.disabled = disableIcloudControls; if (selectIcloudHostPreference) selectIcloudHostPreference.disabled = disableIcloudControls;
@@ -13733,7 +13680,7 @@ function updateButtonStates() {
if (checkboxAutoDeleteIcloud) checkboxAutoDeleteIcloud.disabled = disableIcloudControls; if (checkboxAutoDeleteIcloud) checkboxAutoDeleteIcloud.disabled = disableIcloudControls;
if (btnContributionMode) btnContributionMode.disabled = isContributionButtonLocked(); if (btnContributionMode) btnContributionMode.disabled = isContributionButtonLocked();
applyStepExecutionRangeState(latestState); applyStepExecutionRangeState(latestState);
updateStopButtonState(anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked); updateStopButtonState(anyRunning || isAutoRunPausedPhase() || autoLocked);
renderContributionMode(); renderContributionMode();
} }
@@ -13753,18 +13700,7 @@ function updateStatusDisplay(state) {
displayStatus.textContent = remainingMs > 0 displayStatus.textContent = remainingMs > 0
? `${countdown.title},剩余 ${formatCountdown(remainingMs)}` ? `${countdown.title},剩余 ${formatCountdown(remainingMs)}`
: `${countdown.title},即将结束...`; : `${countdown.title},即将结束...`;
statusBar.classList.add(countdown.tone === 'scheduled' ? 'scheduled' : 'running'); statusBar.classList.add('running');
return;
}
if (isAutoRunScheduledPhase()) {
const remainingMs = Number.isFinite(currentAutoRun.scheduledAt)
? currentAutoRun.scheduledAt - Date.now()
: 0;
displayStatus.textContent = remainingMs > 0
? `自动计划中,剩余 ${formatCountdown(remainingMs)}`
: '倒计时即将结束,正在准备启动...';
statusBar.classList.add('scheduled');
return; return;
} }
@@ -14973,7 +14909,7 @@ btnSaveSettings.addEventListener('click', async () => {
btnStop.addEventListener('click', async () => { btnStop.addEventListener('click', async () => {
btnStop.disabled = true; btnStop.disabled = true;
await chrome.runtime.sendMessage({ type: 'STOP_FLOW', source: 'sidepanel', payload: {} }); await chrome.runtime.sendMessage({ type: 'STOP_FLOW', source: 'sidepanel', payload: {} });
showToast(isAutoRunScheduledPhase() ? '正在取消倒计时计划...' : '正在停止当前流程...', 'warn', 2000); showToast(currentAutoRun.phase === 'waiting_interval' ? '正在取消等待中的倒计时...' : '正在停止当前流程...', 'warn', 2000);
}); });
btnConfigMenu?.addEventListener('click', (event) => { btnConfigMenu?.addEventListener('click', (event) => {
@@ -15150,24 +15086,18 @@ async function startAutoRunFromCurrentSettings() {
btnAutoRun.disabled = true; btnAutoRun.disabled = true;
inputRunCount.disabled = true; inputRunCount.disabled = true;
const delayEnabled = inputAutoDelayEnabled.checked;
const delayMinutes = normalizeAutoDelayMinutes(inputAutoDelayMinutes.value);
const activeFlowId = typeof getSelectedFlowId === 'function' const activeFlowId = typeof getSelectedFlowId === 'function'
? getSelectedFlowId(latestState) ? getSelectedFlowId(latestState)
: (String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID); : (String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID);
const targetId = typeof getSelectedTargetId === 'function' const targetId = typeof getSelectedTargetId === 'function'
? getSelectedTargetId(activeFlowId) ? getSelectedTargetId(activeFlowId)
: normalizeTargetIdForFlow(activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlow(activeFlowId)); : normalizeTargetIdForFlow(activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlow(activeFlowId));
inputAutoDelayMinutes.value = String(delayMinutes); btnAutoRun.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 运行中...';
btnAutoRun.innerHTML = delayEnabled
? '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 计划中...'
: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 运行中...';
const response = await sendSidepanelMessage({ const response = await sendSidepanelMessage({
type: delayEnabled ? 'SCHEDULE_AUTO_RUN' : 'AUTO_RUN', type: 'AUTO_RUN',
source: 'sidepanel', source: 'sidepanel',
payload: { payload: {
totalRuns, totalRuns,
delayMinutes,
activeFlowId, activeFlowId,
targetId, targetId,
autoRunSkipFailures, autoRunSkipFailures,
@@ -15213,15 +15143,12 @@ btnAutoContinue.addEventListener('click', async () => {
btnAutoRunNow?.addEventListener('click', async () => { btnAutoRunNow?.addEventListener('click', async () => {
try { try {
btnAutoRunNow.disabled = true; btnAutoRunNow.disabled = true;
const waitingInterval = currentAutoRun.phase === 'waiting_interval';
await sendSidepanelMessage({ await sendSidepanelMessage({
type: waitingInterval ? 'SKIP_AUTO_RUN_COUNTDOWN' : 'START_SCHEDULED_AUTO_RUN_NOW', type: 'SKIP_AUTO_RUN_COUNTDOWN',
source: 'sidepanel', source: 'sidepanel',
payload: {}, payload: {},
}); });
if (waitingInterval) {
showToast('已跳过当前倒计时,自动流程将立即继续。', 'info', 1800); showToast('已跳过当前倒计时,自动流程将立即继续。', 'info', 1800);
}
} catch (err) { } catch (err) {
showToast(err.message, 'error'); showToast(err.message, 'error');
} finally { } finally {
@@ -15229,18 +15156,6 @@ btnAutoRunNow?.addEventListener('click', async () => {
} }
}); });
btnAutoCancelSchedule?.addEventListener('click', async () => {
try {
btnAutoCancelSchedule.disabled = true;
await chrome.runtime.sendMessage({ type: 'CANCEL_SCHEDULED_AUTO_RUN', source: 'sidepanel', payload: {} });
showToast('已取消倒计时计划。', 'info', 1800);
} catch (err) {
showToast(err.message, 'error');
} finally {
btnAutoCancelSchedule.disabled = false;
}
});
// Reset // Reset
btnReset.addEventListener('click', async () => { btnReset.addEventListener('click', async () => {
const confirmed = await openConfirmModal({ const confirmed = await openConfirmModal({
@@ -15270,7 +15185,6 @@ btnReset.addEventListener('click', async () => {
autoRunCurrentRun: 0, autoRunCurrentRun: 0,
autoRunTotalRuns: 1, autoRunTotalRuns: 1,
autoRunAttemptRun: 0, autoRunAttemptRun: 0,
scheduledAutoRunAt: null,
autoRunCountdownAt: null, autoRunCountdownAt: null,
autoRunCountdownTitle: '', autoRunCountdownTitle: '',
autoRunCountdownNote: '', autoRunCountdownNote: '',
@@ -15460,6 +15374,10 @@ btnOpenKiroRsGithub?.addEventListener('click', () => {
openExternalUrl('https://github.com/QLHazyCoder/kiro.rs'); openExternalUrl('https://github.com/QLHazyCoder/kiro.rs');
}); });
btnOpenWebchat2ApiGithub?.addEventListener('click', () => {
openExternalUrl('https://github.com/zqbxdev/webchat2api');
});
btnGpcHelperBalance?.addEventListener('click', async () => { btnGpcHelperBalance?.addEventListener('click', async () => {
try { try {
const response = await chrome.runtime.sendMessage({ const response = await chrome.runtime.sendMessage({
@@ -16576,12 +16494,6 @@ inputAutoSkipFailuresThreadIntervalMinutes.addEventListener('blur', () => {
saveSettings({ silent: true }).catch(() => { }); saveSettings({ silent: true }).catch(() => { });
}); });
inputAutoDelayEnabled.addEventListener('change', () => {
updateAutoDelayInputState();
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
});
inputStep6CookieCleanupEnabled?.addEventListener('change', () => { inputStep6CookieCleanupEnabled?.addEventListener('change', () => {
markSettingsDirty(true); markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { }); saveSettings({ silent: true }).catch(() => { });
@@ -16655,17 +16567,6 @@ selectFlow?.addEventListener('change', () => {
}); });
}); });
inputAutoDelayMinutes.addEventListener('input', () => {
markSettingsDirty(true);
scheduleSettingsAutoSave();
});
inputAutoDelayMinutes.addEventListener('blur', () => {
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(inputAutoDelayMinutes.value));
saveSettings({ silent: true }).catch(() => { });
});
function getPhoneSmsCountrySelectionForProvider(provider = getSelectedPhoneSmsProvider(), options = {}) { function getPhoneSmsCountrySelectionForProvider(provider = getSelectedPhoneSmsProvider(), options = {}) {
const normalizedProvider = normalizePhoneSmsProvider(provider); const normalizedProvider = normalizePhoneSmsProvider(provider);
const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback; const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback;
@@ -17457,7 +17358,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
password: null, password: null,
nodeStatuses: NODE_DEFAULT_STATUSES, nodeStatuses: NODE_DEFAULT_STATUSES,
logs: [], logs: [],
scheduledAutoRunAt: null,
autoRunCountdownAt: null, autoRunCountdownAt: null,
autoRunCountdownTitle: '', autoRunCountdownTitle: '',
autoRunCountdownNote: '', autoRunCountdownNote: '',
@@ -17492,7 +17392,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
autoRunCurrentRun: 0, autoRunCurrentRun: 0,
autoRunTotalRuns: 1, autoRunTotalRuns: 1,
autoRunAttemptRun: 0, autoRunAttemptRun: 0,
scheduledAutoRunAt: null,
autoRunCountdownAt: null, autoRunCountdownAt: null,
autoRunCountdownTitle: '', autoRunCountdownTitle: '',
autoRunCountdownNote: '', autoRunCountdownNote: '',
@@ -17926,10 +17825,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
inputAutoSkipFailures.checked = Boolean(message.payload.autoRunSkipFailures); inputAutoSkipFailures.checked = Boolean(message.payload.autoRunSkipFailures);
updateFallbackThreadIntervalInputState(); updateFallbackThreadIntervalInputState();
} }
if (message.payload.autoRunDelayEnabled !== undefined) {
inputAutoDelayEnabled.checked = Boolean(message.payload.autoRunDelayEnabled);
updateAutoDelayInputState();
}
if ( if (
message.payload.step6CookieCleanupEnabled !== undefined message.payload.step6CookieCleanupEnabled !== undefined
&& typeof inputStep6CookieCleanupEnabled !== 'undefined' && typeof inputStep6CookieCleanupEnabled !== 'undefined'
@@ -17945,9 +17840,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
renderStepStatuses(latestState); renderStepStatuses(latestState);
updateButtonStates(); updateButtonStates();
} }
if (message.payload.autoRunDelayMinutes !== undefined) {
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(message.payload.autoRunDelayMinutes));
}
if (message.payload.autoRunFallbackThreadIntervalMinutes !== undefined) { if (message.payload.autoRunFallbackThreadIntervalMinutes !== undefined) {
inputAutoSkipFailuresThreadIntervalMinutes.value = String( inputAutoSkipFailuresThreadIntervalMinutes.value = String(
normalizeAutoRunThreadIntervalMinutes(message.payload.autoRunFallbackThreadIntervalMinutes) normalizeAutoRunThreadIntervalMinutes(message.payload.autoRunFallbackThreadIntervalMinutes)
@@ -18239,12 +18131,11 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
case 'AUTO_RUN_STATUS': { case 'AUTO_RUN_STATUS': {
syncLatestState({ syncLatestState({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(message.payload.phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(message.payload.phase),
autoRunPhase: message.payload.phase, autoRunPhase: message.payload.phase,
autoRunCurrentRun: message.payload.currentRun, autoRunCurrentRun: message.payload.currentRun,
autoRunTotalRuns: message.payload.totalRuns, autoRunTotalRuns: message.payload.totalRuns,
autoRunAttemptRun: message.payload.attemptRun, autoRunAttemptRun: message.payload.attemptRun,
scheduledAutoRunAt: message.payload.scheduledAt ?? null,
autoRunCountdownAt: message.payload.countdownAt ?? null, autoRunCountdownAt: message.payload.countdownAt ?? null,
autoRunCountdownTitle: message.payload.countdownTitle ?? '', autoRunCountdownTitle: message.payload.countdownTitle ?? '',
autoRunCountdownNote: message.payload.countdownNote ?? '', autoRunCountdownNote: message.payload.countdownNote ?? '',
@@ -18252,7 +18143,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
applyAutoRunStatus(message.payload); applyAutoRunStatus(message.payload);
updateStatusDisplay(latestState); updateStatusDisplay(latestState);
updateButtonStates(); updateButtonStates();
if (!['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(message.payload.phase)) { if (!['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(message.payload.phase)) {
scheduleAccountRunHistoryRefresh(); scheduleAccountRunHistoryRefresh();
} }
break; break;
+16 -32
View File
@@ -134,8 +134,6 @@ test('auto-run controller skips add-phone failures to the next round instead of
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: '163', mailProvider: '163',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -185,7 +183,7 @@ test('auto-run controller skips add-phone failures to the next round instead of
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -201,7 +199,7 @@ test('auto-run controller skips add-phone failures to the next round instead of
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -297,8 +295,6 @@ test('auto-run controller treats phone-number supply exhaustion as round-fatal a
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: '163', mailProvider: '163',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -348,7 +344,7 @@ test('auto-run controller treats phone-number supply exhaustion as round-fatal a
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -364,7 +360,7 @@ test('auto-run controller treats phone-number supply exhaustion as round-fatal a
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -460,8 +456,6 @@ test('auto-run controller treats ended GPC task as round-fatal and skips same-ro
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: '163', mailProvider: '163',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -511,7 +505,7 @@ test('auto-run controller treats ended GPC task as round-fatal and skips same-ro
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -527,7 +521,7 @@ test('auto-run controller treats ended GPC task as round-fatal and skips same-ro
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -621,8 +615,6 @@ test('auto-run controller keeps same-round retrying for step9 local replacement
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: '163', mailProvider: '163',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -672,7 +664,7 @@ test('auto-run controller keeps same-round retrying for step9 local replacement
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -688,7 +680,7 @@ test('auto-run controller keeps same-round retrying for step9 local replacement
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -779,8 +771,6 @@ test('auto-run controller skips user_already_exists failures to the next round i
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: '163', mailProvider: '163',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -830,7 +820,7 @@ test('auto-run controller skips user_already_exists failures to the next round i
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -846,7 +836,7 @@ test('auto-run controller skips user_already_exists failures to the next round i
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -943,8 +933,6 @@ test('auto-run controller skips step 4 repeated 405 recovery failures to the nex
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: '163', mailProvider: '163',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -994,7 +982,7 @@ test('auto-run controller skips step 4 repeated 405 recovery failures to the nex
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -1010,7 +998,7 @@ test('auto-run controller skips step 4 repeated 405 recovery failures to the nex
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -1108,8 +1096,6 @@ test('auto-run controller keeps retrying the same custom mail provider pool emai
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: 'custom', mailProvider: 'custom',
customMailProviderPool: ['first@example.com'], customMailProviderPool: ['first@example.com'],
@@ -1160,7 +1146,7 @@ test('auto-run controller keeps retrying the same custom mail provider pool emai
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -1176,7 +1162,7 @@ test('auto-run controller keeps retrying the same custom mail provider pool emai
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -1274,8 +1260,6 @@ test('auto-run controller retries 5sim rate limit failures instead of treating c
customPassword: '', customPassword: '',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: '163', mailProvider: '163',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -1325,7 +1309,7 @@ test('auto-run controller retries 5sim rate limit failures instead of treating c
events.broadcasts.push({ phase, ...payload }); events.broadcasts.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -1341,7 +1325,7 @@ test('auto-run controller retries 5sim rate limit failures instead of treating c
return sessionSeed; return sessionSeed;
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -130,8 +130,6 @@ let currentState = {
customPassword: '', customPassword: '',
autoRunSkipFailures: false, autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
signupMethod: 'phone', signupMethod: 'phone',
resolvedSignupMethod: 'phone', resolvedSignupMethod: 'phone',
@@ -204,8 +202,6 @@ async function resetState() {
customPassword: prev.customPassword, customPassword: prev.customPassword,
autoRunSkipFailures: prev.autoRunSkipFailures, autoRunSkipFailures: prev.autoRunSkipFailures,
autoRunFallbackThreadIntervalMinutes: prev.autoRunFallbackThreadIntervalMinutes, autoRunFallbackThreadIntervalMinutes: prev.autoRunFallbackThreadIntervalMinutes,
autoRunDelayEnabled: prev.autoRunDelayEnabled,
autoRunDelayMinutes: prev.autoRunDelayMinutes,
autoStepDelaySeconds: prev.autoStepDelaySeconds, autoStepDelaySeconds: prev.autoStepDelaySeconds,
signupMethod: prev.signupMethod, signupMethod: prev.signupMethod,
resolvedSignupMethod: null, resolvedSignupMethod: null,
+2 -4
View File
@@ -104,8 +104,6 @@ test('auto-run controller verifies hotmail mailbox before each fresh attempt sta
customPassword: '', customPassword: '',
autoRunSkipFailures: false, autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
mailProvider: 'hotmail-api', mailProvider: 'hotmail-api',
emailGenerator: 'duck', emailGenerator: 'duck',
@@ -149,7 +147,7 @@ test('auto-run controller verifies hotmail mailbox before each fresh attempt sta
broadcastAutoRunStatus: async (phase, payload = {}) => { broadcastAutoRunStatus: async (phase, payload = {}) => {
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun, autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns, autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -169,7 +167,7 @@ test('auto-run controller verifies hotmail mailbox before each fresh attempt sta
events.preflightCalls.push({ ...payload }); events.preflightCalls.push({ ...payload });
}, },
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
+4 -10
View File
@@ -36,8 +36,6 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
phoneSignupReloginAfterBindEmailEnabled: false, phoneSignupReloginAfterBindEmailEnabled: false,
autoRunSkipFailures: false, autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
signupMethod: 'email', signupMethod: 'email',
stepExecutionRangeByFlow: { stepExecutionRangeByFlow: {
@@ -94,7 +92,7 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
currentState = { currentState = {
...currentState, ...currentState,
...extraState, ...extraState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? currentState.autoRunTotalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? currentState.autoRunTotalRuns ?? 1,
@@ -124,7 +122,7 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
}, },
ensureHotmailMailboxReadyForAutoRunRound: async () => {}, ensureHotmailMailboxReadyForAutoRunRound: async () => {},
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -179,8 +177,6 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
phoneSignupReloginAfterBindEmailEnabled: false, phoneSignupReloginAfterBindEmailEnabled: false,
autoRunSkipFailures: false, autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
signupMethod: 'email', signupMethod: 'email',
stepExecutionRangeByFlow: { stepExecutionRangeByFlow: {
@@ -445,8 +441,6 @@ test('auto-run controller stops immediately on kiro proxy failures even when ski
flowId: 'kiro', flowId: 'kiro',
autoRunSkipFailures: true, autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0, autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null, autoStepDelaySeconds: null,
nodeStatuses: { nodeStatuses: {
'kiro-open-register-page': 'pending', 'kiro-open-register-page': 'pending',
@@ -496,7 +490,7 @@ test('auto-run controller stops immediately on kiro proxy failures even when ski
events.phases.push({ phase, ...payload }); events.phases.push({ phase, ...payload });
currentState = { currentState = {
...currentState, ...currentState,
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? currentState.autoRunTotalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? currentState.autoRunTotalRuns ?? 1,
@@ -514,7 +508,7 @@ test('auto-run controller stops immediately on kiro proxy failures even when ski
}, },
ensureHotmailMailboxReadyForAutoRunRound: async () => {}, ensureHotmailMailboxReadyForAutoRunRound: async () => {},
getAutoRunStatusPayload: (phase, payload = {}) => ({ getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase), autoRunning: ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
autoRunPhase: phase, autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0, autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1, autoRunTotalRuns: payload.totalRuns ?? 1,
+5 -112
View File
@@ -63,7 +63,6 @@ const helperBundle = [
test('launchAutoRunTimerPlan ignores stale timer plans after stop invalidates the session', async () => { test('launchAutoRunTimerPlan ignores stale timer plans after stop invalidates the session', async () => {
const api = new Function(` const api = new Function(`
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
const AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS = 'between_rounds'; const AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS = 'between_rounds';
const AUTO_RUN_TIMER_KIND_BEFORE_RETRY = 'before_retry'; const AUTO_RUN_TIMER_KIND_BEFORE_RETRY = 'before_retry';
const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3; const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3;
@@ -76,14 +75,15 @@ let autoRunAttemptRun = 0;
let autoRunSessionId = 0; let autoRunSessionId = 0;
const state = { const state = {
autoRunDelayEnabled: false,
autoRunTimerPlan: { autoRunTimerPlan: {
kind: AUTO_RUN_TIMER_KIND_SCHEDULED_START, kind: AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
fireAt: Date.now() + 60_000, fireAt: Date.now() + 60_000,
totalRuns: 2, totalRuns: 2,
autoRunSkipFailures: false, autoRunSkipFailures: false,
autoRunSessionId: 42, autoRunSessionId: 42,
countdownTitle: '已计划自动运行', currentRun: 1,
attemptRun: 1,
countdownTitle: '等待继续',
countdownNote: '等待启动', countdownNote: '等待启动',
}, },
}; };
@@ -106,7 +106,6 @@ async function clearAutoRunTimerAlarm() {
async function broadcastAutoRunStatus() {} async function broadcastAutoRunStatus() {}
async function addLog() {} async function addLog() {}
async function setAutoRunDelayEnabledState() {}
function serializeAutoRunRoundSummaries(totalRuns, summaries = []) { function serializeAutoRunRoundSummaries(totalRuns, summaries = []) {
return Array.isArray(summaries) ? summaries : []; return Array.isArray(summaries) ? summaries : [];
} }
@@ -137,116 +136,10 @@ return {
const started = await api.launchAutoRunTimerPlan('alarm'); const started = await api.launchAutoRunTimerPlan('alarm');
const snapshot = api.snapshot(); const snapshot = api.snapshot();
assert.equal(started, false);
assert.equal(snapshot.startCalls, 0, 'stale timer plan should not restart auto-run');
assert.equal(snapshot.clearStopCalls, 0, 'stale timer plan should not clear the stop flag for a cancelled run');
assert.equal(snapshot.clearAlarmCalls, 0, 'stale timer plan should not clear a potentially newer alarm');
assert.equal(snapshot.autoRunCurrentRun, 0);
assert.equal(snapshot.autoRunTotalRuns, 1);
assert.equal(snapshot.autoRunAttemptRun, 0);
});
test('launchAutoRunTimerPlan cancels an invalid scheduled start before restarting auto-run', async () => {
const api = new Function(`
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
const AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS = 'between_rounds';
const AUTO_RUN_TIMER_KIND_BEFORE_RETRY = 'before_retry';
const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3;
let autoRunTimerLaunching = false;
let autoRunActive = false;
let autoRunCurrentRun = 0;
let autoRunTotalRuns = 1;
let autoRunAttemptRun = 0;
let autoRunSessionId = 0;
const state = {
activeFlowId: 'site-a',
panelMode: 'cpa',
signupMethod: 'phone',
autoRunDelayEnabled: false,
autoRunTimerPlan: {
kind: AUTO_RUN_TIMER_KIND_SCHEDULED_START,
fireAt: Date.now() + 60_000,
totalRuns: 2,
autoRunSkipFailures: false,
autoRunSessionId: 0,
countdownTitle: '已计划自动运行',
countdownNote: '等待启动',
},
};
let startCalls = 0;
let clearStopCalls = 0;
let clearAlarmCalls = 0;
const broadcasts = [];
const logs = [];
async function getState() {
return { ...state };
}
function getPendingAutoRunTimerPlan() {
return state.autoRunTimerPlan;
}
async function clearAutoRunTimerAlarm() {
clearAlarmCalls += 1;
}
async function broadcastAutoRunStatus(phase, statusPayload, statePayload) {
broadcasts.push({ phase, statusPayload, statePayload });
}
async function addLog(message, level) {
logs.push({ message, level });
}
async function setAutoRunDelayEnabledState() {}
function serializeAutoRunRoundSummaries(totalRuns, summaries = []) {
return Array.isArray(summaries) ? summaries : [];
}
function clearStopRequest() {
clearStopCalls += 1;
}
function startAutoRunLoop() {
startCalls += 1;
}
function validateAutoRunStartState() {
return {
ok: false,
errors: [{ message: '当前 flow 不支持手机号注册。' }],
};
}
${helperBundle}
return {
launchAutoRunTimerPlan,
snapshot() {
return {
startCalls,
clearStopCalls,
clearAlarmCalls,
broadcasts,
logs,
autoRunCurrentRun,
autoRunTotalRuns,
autoRunAttemptRun,
};
},
};
`)();
const started = await api.launchAutoRunTimerPlan('alarm');
const snapshot = api.snapshot();
assert.equal(started, false); assert.equal(started, false);
assert.equal(snapshot.startCalls, 0); assert.equal(snapshot.startCalls, 0);
assert.equal(snapshot.clearStopCalls, 0); assert.equal(snapshot.clearStopCalls, 0);
assert.equal(snapshot.clearAlarmCalls, 1); assert.equal(snapshot.clearAlarmCalls, 0);
assert.equal(snapshot.broadcasts.length, 1);
assert.equal(snapshot.broadcasts[0].phase, 'idle');
assert.match(snapshot.logs[0].message, /自动运行计划已取消:当前 flow 不支持手机号注册。/);
assert.equal(snapshot.logs[0].level, 'error');
assert.equal(snapshot.autoRunCurrentRun, 0); assert.equal(snapshot.autoRunCurrentRun, 0);
assert.equal(snapshot.autoRunTotalRuns, 1); assert.equal(snapshot.autoRunTotalRuns, 1);
assert.equal(snapshot.autoRunAttemptRun, 0); assert.equal(snapshot.autoRunAttemptRun, 0);
@@ -196,7 +196,6 @@ const PERSISTED_SETTING_DEFAULTS = {
function normalizePanelMode(value) { return value === 'sub2api' ? 'sub2api' : (value === 'codex2api' ? 'codex2api' : 'cpa'); } function normalizePanelMode(value) { return value === 'sub2api' ? 'sub2api' : (value === 'codex2api' ? 'codex2api' : 'cpa'); }
function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; } function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoRunDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value == null || value === '' ? null : Number(value); } function normalizeAutoStepDelaySeconds(value) { return value == null || value === '' ? null : Number(value); }
function normalizeMailProvider(value) { return String(value || '').trim().toLowerCase() || '163'; } function normalizeMailProvider(value) { return String(value || '').trim().toLowerCase() || '163'; }
function normalizeMail2925Mode(value) { return String(value || '').trim().toLowerCase() === 'receive' ? 'receive' : 'provide'; } function normalizeMail2925Mode(value) { return String(value || '').trim().toLowerCase() === 'receive' ? 'receive' : 'provide'; }
@@ -82,7 +82,6 @@ function normalizeSignupMethod(value = '') { return String(value || '').trim().t
function resolveSignupMethod(state = {}) { return normalizeSignupMethod(state?.signupMethod); } function resolveSignupMethod(state = {}) { return normalizeSignupMethod(state?.signupMethod); }
function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; } function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoRunDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value, fallback = null) { return value == null || value === '' ? fallback : Number(value); } function normalizeAutoStepDelaySeconds(value, fallback = null) { return value == null || value === '' ? fallback : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; } function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; }
function normalizeMailProvider(value) { return String(value || '').trim().toLowerCase() || '163'; } function normalizeMailProvider(value) { return String(value || '').trim().toLowerCase() || '163'; }
@@ -180,7 +179,6 @@ function normalizeSignupMethod(value = '') { return String(value || '').trim().t
function resolveSignupMethod(state = {}) { return normalizeSignupMethod(state?.signupMethod); } function resolveSignupMethod(state = {}) { return normalizeSignupMethod(state?.signupMethod); }
function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; } function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoRunDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value, fallback = null) { return value == null || value === '' ? fallback : Number(value); } function normalizeAutoStepDelaySeconds(value, fallback = null) { return value == null || value === '' ? fallback : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; } function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; }
function normalizeMailProvider(value) { return String(value || '').trim().toLowerCase() || '163'; } function normalizeMailProvider(value) { return String(value || '').trim().toLowerCase() || '163'; }
+1 -18
View File
@@ -372,7 +372,7 @@ test('message router re-syncs contribution mode before AUTO_RUN when sidepanel p
]); ]);
}); });
test('message router blocks AUTO_RUN and SCHEDULE_AUTO_RUN when shared auto-run validation fails', async () => { test('message router blocks AUTO_RUN when shared auto-run validation fails', async () => {
const source = fs.readFileSync('background/message-router.js', 'utf8'); const source = fs.readFileSync('background/message-router.js', 'utf8');
const globalScope = {}; const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundMessageRouter;`)(globalScope); const api = new Function('self', `${source}; return self.MultiPageBackgroundMessageRouter;`)(globalScope);
@@ -388,10 +388,6 @@ test('message router blocks AUTO_RUN and SCHEDULE_AUTO_RUN when shared auto-run
stepStatuses: {}, stepStatuses: {},
}), }),
normalizeRunCount: (value) => Number(value) || 1, normalizeRunCount: (value) => Number(value) || 1,
scheduleAutoRun: async () => {
calls.push({ type: 'scheduleAutoRun' });
return { ok: true };
},
setState: async (updates) => { setState: async (updates) => {
calls.push({ type: 'setState', updates }); calls.push({ type: 'setState', updates });
}, },
@@ -415,19 +411,6 @@ test('message router blocks AUTO_RUN and SCHEDULE_AUTO_RUN when shared auto-run
}), }),
/当前 flow 不支持手机号注册。/ /当前 flow 不支持手机号注册。/
); );
await assert.rejects(
() => router.handleMessage({
type: 'SCHEDULE_AUTO_RUN',
payload: {
totalRuns: 2,
delayMinutes: 5,
autoRunSkipFailures: false,
},
}),
/当前 flow 不支持手机号注册。/
);
assert.deepStrictEqual(calls, []); assert.deepStrictEqual(calls, []);
}); });
-3
View File
@@ -99,9 +99,6 @@ function normalizeMailProvider(value = '') {
function normalizeAutoRunFallbackThreadIntervalMinutes(value) { function normalizeAutoRunFallbackThreadIntervalMinutes(value) {
return Math.max(0, Math.floor(Number(value) || 0)); return Math.max(0, Math.floor(Number(value) || 0));
} }
function normalizeAutoRunDelayMinutes(value) {
return Math.max(1, Math.floor(Number(value) || 30));
}
function normalizeAutoStepDelaySeconds(value, fallback = null) { function normalizeAutoStepDelaySeconds(value, fallback = null) {
const numeric = Number(value); const numeric = Number(value);
return Number.isFinite(numeric) ? Math.max(0, Math.floor(numeric)) : fallback; return Number.isFinite(numeric) ? Math.max(0, Math.floor(numeric)) : fallback;
@@ -35,7 +35,6 @@ function createRouterWithFinalNode(options = {}) {
buildLuckmailSessionSettingsPayload: () => ({}), buildLuckmailSessionSettingsPayload: () => ({}),
buildPersistentSettingsPayload: () => ({}), buildPersistentSettingsPayload: () => ({}),
broadcastDataUpdate: () => {}, broadcastDataUpdate: () => {},
cancelScheduledAutoRun: async () => {},
checkIcloudSession: async () => {}, checkIcloudSession: async () => {},
clearAutoRunTimerAlarm: async () => {}, clearAutoRunTimerAlarm: async () => {},
clearLuckmailRuntimeState: async () => {}, clearLuckmailRuntimeState: async () => {},
@@ -87,7 +86,6 @@ function createRouterWithFinalNode(options = {}) {
listLuckmailPurchasesForManagement: async () => [], listLuckmailPurchasesForManagement: async () => [],
normalizeHotmailAccounts: (items) => items, normalizeHotmailAccounts: (items) => items,
normalizeRunCount: (value) => value, normalizeRunCount: (value) => value,
AUTO_RUN_TIMER_KIND_SCHEDULED_START: 'scheduled',
notifyNodeComplete: () => {}, notifyNodeComplete: () => {},
notifyNodeError: () => {}, notifyNodeError: () => {},
patchHotmailAccount: async () => {}, patchHotmailAccount: async () => {},
@@ -96,7 +94,6 @@ function createRouterWithFinalNode(options = {}) {
requestStop: async () => {}, requestStop: async () => {},
resetState: async () => {}, resetState: async () => {},
resumeAutoRun: async () => {}, resumeAutoRun: async () => {},
scheduleAutoRun: async () => {},
selectLuckmailPurchase: async () => {}, selectLuckmailPurchase: async () => {},
setCurrentHotmailAccount: async () => {}, setCurrentHotmailAccount: async () => {},
setCurrentMail2925Account: async () => {}, setCurrentMail2925Account: async () => {},
@@ -103,7 +103,6 @@ function createRouter(overrides = {}) {
broadcastDataUpdate: (updates) => { broadcastDataUpdate: (updates) => {
events.broadcasts.push(updates); events.broadcasts.push(updates);
}, },
cancelScheduledAutoRun: async () => {},
checkIcloudSession: async () => {}, checkIcloudSession: async () => {},
clearAutoRunTimerAlarm: async () => {}, clearAutoRunTimerAlarm: async () => {},
clearLuckmailRuntimeState: async () => {}, clearLuckmailRuntimeState: async () => {},
@@ -171,7 +170,6 @@ function createRouter(overrides = {}) {
listLuckmailPurchasesForManagement: async () => [], listLuckmailPurchasesForManagement: async () => [],
normalizeHotmailAccounts: (items) => items, normalizeHotmailAccounts: (items) => items,
normalizeRunCount: (value) => value, normalizeRunCount: (value) => value,
AUTO_RUN_TIMER_KIND_SCHEDULED_START: 'scheduled',
notifyNodeComplete: (nodeId, payload) => { notifyNodeComplete: (nodeId, payload) => {
events.notifyCompletions.push({ step: getStepForNode(nodeId), nodeId, payload }); events.notifyCompletions.push({ step: getStepForNode(nodeId), nodeId, payload });
}, },
@@ -183,7 +181,6 @@ function createRouter(overrides = {}) {
requestStop: async () => {}, requestStop: async () => {},
resetState: async () => {}, resetState: async () => {},
resumeAutoRun: async () => {}, resumeAutoRun: async () => {},
scheduleAutoRun: async () => {},
selectLuckmailPurchase: async () => {}, selectLuckmailPurchase: async () => {},
setCurrentHotmailAccount: async () => {}, setCurrentHotmailAccount: async () => {},
setEmailState: async (email) => { setEmailState: async (email) => {
-2
View File
@@ -195,7 +195,6 @@ let step8TabUpdatedListener = null;
let step8PendingReject = null; let step8PendingReject = null;
let resumeWaiter = null; let resumeWaiter = null;
const STOP_ERROR_MESSAGE = '流程已被用户停止。'; const STOP_ERROR_MESSAGE = '流程已被用户停止。';
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
const DEFAULT_STATE = { const DEFAULT_STATE = {
stepStatuses: Object.fromEntries([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((step) => [step, 'pending'])), stepStatuses: Object.fromEntries([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((step) => [step, 'pending'])),
}; };
@@ -218,7 +217,6 @@ function abortActiveIcloudRequests() {}
function getPendingAutoRunTimerPlan() { function getPendingAutoRunTimerPlan() {
return null; return null;
} }
async function cancelScheduledAutoRun() {}
async function clearAutoRunTimerAlarm() {} async function clearAutoRunTimerAlarm() {}
function clearStopRequest() { function clearStopRequest() {
stopRequested = false; stopRequested = false;
+2 -2
View File
@@ -94,7 +94,7 @@ test('flow capability registry exposes Kiro as an independent flow with its own
assert.deepEqual(capabilityState.flowCapabilities.contributionAdapterIds, ['kiro-builder-id']); assert.deepEqual(capabilityState.flowCapabilities.contributionAdapterIds, ['kiro-builder-id']);
assert.deepEqual( assert.deepEqual(
capabilityState.visibleGroupIds, capabilityState.visibleGroupIds,
['kiro-runtime-status', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy'] ['kiro-runtime-status', 'shared-auto-run', 'shared-settings-actions', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy']
); );
}); });
@@ -124,7 +124,7 @@ test('flow capability registry exposes Grok as an independent SSO flow without O
assert.deepEqual(capabilityState.flowCapabilities.contributionAdapterIds, []); assert.deepEqual(capabilityState.flowCapabilities.contributionAdapterIds, []);
assert.deepEqual( assert.deepEqual(
capabilityState.visibleGroupIds, capabilityState.visibleGroupIds,
['grok-runtime-status', 'grok-target-webchat2api', 'service-account', 'service-email', 'service-proxy'] ['grok-runtime-status', 'shared-auto-run', 'shared-settings-actions', 'grok-target-webchat2api', 'service-account', 'service-email', 'service-proxy']
); );
}); });
+23 -3
View File
@@ -21,20 +21,32 @@ test('flow registry exposes canonical flow and target metadata', () => {
assert.equal(flowRegistry.normalizeFlowId('grok'), 'grok'); assert.equal(flowRegistry.normalizeFlowId('grok'), 'grok');
assert.equal(flowRegistry.normalizeFlowId('unknown'), 'openai'); assert.equal(flowRegistry.normalizeFlowId('unknown'), 'openai');
assert.equal(flowRegistry.getFlowLabel('openai'), 'Codex / OpenAI'); assert.equal(flowRegistry.getFlowLabel('openai'), 'Codex / OpenAI');
assert.deepEqual(
flowRegistry.getFlowDefinition('openai')?.settingsDefaults?.autoRun?.stepExecutionRange,
{ enabled: false, fromStep: 1, toStep: 11 }
);
assert.deepEqual(
flowRegistry.getFlowDefinition('openai')?.targets?.cpa?.defaultState,
{ vpsUrl: '', vpsPassword: '', localCpaStep9Mode: 'submit' }
);
assert.deepEqual(
flowRegistry.getFlowDefinition('kiro')?.targets?.['kiro-rs']?.defaultState,
{ baseUrl: '', apiKey: '' }
);
assert.equal(flowRegistry.normalizeTargetId('openai', 'sub2api'), 'sub2api'); assert.equal(flowRegistry.normalizeTargetId('openai', 'sub2api'), 'sub2api');
assert.equal(flowRegistry.normalizeTargetId('kiro', 'anything-else'), 'kiro-rs'); assert.equal(flowRegistry.normalizeTargetId('kiro', 'anything-else'), 'kiro-rs');
assert.equal(flowRegistry.normalizeTargetId('grok', 'anything-else'), 'webchat2api'); assert.equal(flowRegistry.normalizeTargetId('grok', 'anything-else'), 'webchat2api');
assert.deepEqual( assert.deepEqual(
flowRegistry.getVisibleGroupIds('openai', 'cpa'), flowRegistry.getVisibleGroupIds('openai', 'cpa'),
['openai-plus', 'openai-phone', 'openai-oauth', 'openai-step6', 'openai-target-cpa', 'service-account', 'service-email', 'service-proxy'] ['openai-plus', 'openai-phone', 'shared-auto-run', 'openai-oauth', 'openai-step6', 'shared-settings-actions', 'openai-target-cpa', 'service-account', 'service-email', 'service-proxy']
); );
assert.deepEqual( assert.deepEqual(
flowRegistry.getVisibleGroupIds('kiro', 'kiro-rs'), flowRegistry.getVisibleGroupIds('kiro', 'kiro-rs'),
['kiro-runtime-status', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy'] ['kiro-runtime-status', 'shared-auto-run', 'shared-settings-actions', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy']
); );
assert.deepEqual( assert.deepEqual(
flowRegistry.getVisibleGroupIds('grok', 'webchat2api'), flowRegistry.getVisibleGroupIds('grok', 'webchat2api'),
['grok-runtime-status', 'grok-target-webchat2api', 'service-account', 'service-email', 'service-proxy'] ['grok-runtime-status', 'shared-auto-run', 'shared-settings-actions', 'grok-target-webchat2api', 'service-account', 'service-email', 'service-proxy']
); );
assert.deepEqual( assert.deepEqual(
flowRegistry.getTargetOptions('openai').map((entry) => entry.id), flowRegistry.getTargetOptions('openai').map((entry) => entry.id),
@@ -48,6 +60,14 @@ test('flow registry exposes canonical flow and target metadata', () => {
flowRegistry.getSettingsGroupDefinition('openai-plus')?.rowIds, flowRegistry.getSettingsGroupDefinition('openai-plus')?.rowIds,
['row-plus-mode', 'row-plus-account-access-strategy', 'row-plus-payment-method'] ['row-plus-mode', 'row-plus-account-access-strategy', 'row-plus-payment-method']
); );
assert.deepEqual(
flowRegistry.getSettingsGroupDefinition('shared-auto-run')?.rowIds,
['row-shared-auto-run', 'row-auto-run-thread-interval', 'row-step-execution-range']
);
assert.deepEqual(
flowRegistry.getSettingsGroupDefinition('shared-settings-actions')?.rowIds,
['row-settings-actions']
);
assert.equal(flowRegistry.getPublicationTargetDefinition('kiro', 'kiro-rs')?.label, 'kiro.rs'); assert.equal(flowRegistry.getPublicationTargetDefinition('kiro', 'kiro-rs')?.label, 'kiro.rs');
assert.equal(flowRegistry.getFlowCapabilities('openai').supportsAccountContribution, true); assert.equal(flowRegistry.getFlowCapabilities('openai').supportsAccountContribution, true);
assert.equal(flowRegistry.getFlowCapabilities('kiro').supportsAccountContribution, true); assert.equal(flowRegistry.getFlowCapabilities('kiro').supportsAccountContribution, true);
+75 -1
View File
@@ -78,6 +78,9 @@ test('grok verification runner polls by flow node and submits normalized code',
registerTab: async () => {}, registerTab: async () => {},
sendToContentScriptResilient: async (sourceId, message) => { sendToContentScriptResilient: async (sourceId, message) => {
calls.push({ type: 'send', sourceId, message }); calls.push({ type: 'send', sourceId, message });
if (message.nodeId === 'GET_PAGE_STATE') {
return { submitted: true, state: 'verification_code_entry', url: 'https://accounts.x.ai/verify' };
}
return { submitted: true, state: 'profile_entry', url: 'https://accounts.x.ai/profile' }; return { submitted: true, state: 'profile_entry', url: 'https://accounts.x.ai/profile' };
}, },
setState: async (patch) => { setState: async (patch) => {
@@ -97,7 +100,9 @@ test('grok verification runner polls by flow node and submits normalized code',
assert.equal(pollCall.options.state.activeFlowId, 'grok'); assert.equal(pollCall.options.state.activeFlowId, 'grok');
assert.equal(pollCall.options.state.visibleStep, 3); assert.equal(pollCall.options.state.visibleStep, 3);
const sendCall = calls.find((entry) => entry.type === 'send'); const sendCall = calls.find((entry) => (
entry.type === 'send' && entry.message.nodeId === 'grok-submit-verification-code'
));
assert.equal(sendCall.sourceId, 'grok-register-page'); assert.equal(sendCall.sourceId, 'grok-register-page');
assert.equal(sendCall.message.type, 'EXECUTE_NODE'); assert.equal(sendCall.message.type, 'EXECUTE_NODE');
assert.equal(sendCall.message.nodeId, 'grok-submit-verification-code'); assert.equal(sendCall.message.nodeId, 'grok-submit-verification-code');
@@ -110,6 +115,75 @@ test('grok verification runner polls by flow node and submits normalized code',
assert.equal(getGrokRuntime(completedPayload).register.status, 'verified'); assert.equal(getGrokRuntime(completedPayload).register.status, 'verified');
}); });
test('grok verification runner waits for verification page before polling mail', async () => {
const api = loadGrokRunnerApi();
let pollCalled = false;
let currentState = {
activeFlowId: 'grok',
grokRegisterTabId: 102,
grokEmail: 'grok-user@example.com',
grokVerificationRequestedAt: 1000000,
runtimeState: {
flowState: {
grok: {
session: {
registerTabId: 102,
},
register: {
email: 'grok-user@example.com',
verificationRequestedAt: 1000000,
},
},
},
},
};
const originalDateNow = Date.now;
let fakeNow = 1000000;
const runner = api.createGrokRegisterRunner({
addLog: async () => {},
chrome: {
tabs: {
get: async (tabId) => ({ id: tabId }),
update: async () => {},
},
},
completeNodeFromBackground: async () => {},
ensureContentScriptReadyOnTab: async () => {},
getState: async () => currentState,
getTabId: async () => 102,
isTabAlive: async () => true,
pollFlowVerificationCode: async () => {
pollCalled = true;
return { code: 'ABC-123', messageId: 'mail-001' };
},
registerTab: async () => {},
sendToContentScriptResilient: async (_sourceId, message) => {
if (message.nodeId === 'GET_PAGE_STATE') {
return { submitted: true, state: 'email_entry', url: 'https://accounts.x.ai/sign-up' };
}
return { submitted: true, state: 'profile_entry', url: 'https://accounts.x.ai/profile' };
},
setState: async (patch) => {
currentState = { ...currentState, ...patch };
},
sleepWithStop: async (ms = 0) => {
fakeNow += Number(ms) || 1000;
},
waitForTabStableComplete: async () => {},
});
Date.now = () => fakeNow;
try {
await assert.rejects(
() => runner.executeGrokSubmitVerificationCode({ nodeId: 'grok-submit-verification-code', ...currentState }),
/尚未进入验证码页面/
);
} finally {
Date.now = originalDateNow;
}
assert.equal(pollCalled, false);
});
test('grok SSO extraction stores only the current cookie without logging the secret value', async () => { test('grok SSO extraction stores only the current cookie without logging the secret value', async () => {
const api = loadGrokRunnerApi(); const api = loadGrokRunnerApi();
const logs = []; const logs = [];
+2 -8
View File
@@ -85,7 +85,6 @@ test('sidepanel idle auto-run status does not reset manual run count input', ()
extractFunction(source, 'syncAutoRunState'), extractFunction(source, 'syncAutoRunState'),
extractFunction(source, 'isAutoRunLockedPhase'), extractFunction(source, 'isAutoRunLockedPhase'),
extractFunction(source, 'isAutoRunPausedPhase'), extractFunction(source, 'isAutoRunPausedPhase'),
extractFunction(source, 'isAutoRunScheduledPhase'),
extractFunction(source, 'applyAutoRunStatus'), extractFunction(source, 'applyAutoRunStatus'),
].join('\n'); ].join('\n');
@@ -96,7 +95,6 @@ let currentAutoRun = {
currentRun: 0, currentRun: 0,
totalRuns: 1, totalRuns: 1,
attemptRun: 0, attemptRun: 0,
scheduledAt: null,
countdownAt: null, countdownAt: null,
countdownTitle: '', countdownTitle: '',
countdownNote: '', countdownNote: '',
@@ -116,9 +114,8 @@ function getLockedRunCountFromEmailPool() { return lockedRunCount; }
function isCustomMailProvider() { return false; } function isCustomMailProvider() { return false; }
function usesCustomEmailPoolGenerator() { return false; } function usesCustomEmailPoolGenerator() { return false; }
function setDefaultAutoRunButton() {} function setDefaultAutoRunButton() {}
function updateAutoDelayInputState() {}
function updateFallbackThreadIntervalInputState() {} function updateFallbackThreadIntervalInputState() {}
function syncScheduledCountdownTicker() {} function syncAutoRunCountdownTicker() {}
function updateStopButtonState() {} function updateStopButtonState() {}
function getStepStatuses() { return {}; } function getStepStatuses() { return {}; }
function updateConfigMenuControls() {} function updateConfigMenuControls() {}
@@ -171,7 +168,6 @@ test('sidepanel pending auto-run start ignores stale active run count sync', ()
extractFunction(source, 'syncAutoRunState'), extractFunction(source, 'syncAutoRunState'),
extractFunction(source, 'isAutoRunLockedPhase'), extractFunction(source, 'isAutoRunLockedPhase'),
extractFunction(source, 'isAutoRunPausedPhase'), extractFunction(source, 'isAutoRunPausedPhase'),
extractFunction(source, 'isAutoRunScheduledPhase'),
extractFunction(source, 'isAutoRunSourceSyncPhase'), extractFunction(source, 'isAutoRunSourceSyncPhase'),
extractFunction(source, 'shouldSyncRunCountFromAutoRunSource'), extractFunction(source, 'shouldSyncRunCountFromAutoRunSource'),
extractFunction(source, 'applyAutoRunStatus'), extractFunction(source, 'applyAutoRunStatus'),
@@ -184,7 +180,6 @@ let currentAutoRun = {
currentRun: 0, currentRun: 0,
totalRuns: 1, totalRuns: 1,
attemptRun: 0, attemptRun: 0,
scheduledAt: null,
countdownAt: null, countdownAt: null,
countdownTitle: '', countdownTitle: '',
countdownNote: '', countdownNote: '',
@@ -205,9 +200,8 @@ function getLockedRunCountFromEmailPool() { return 0; }
function isCustomMailProvider() { return false; } function isCustomMailProvider() { return false; }
function usesCustomEmailPoolGenerator() { return false; } function usesCustomEmailPoolGenerator() { return false; }
function setDefaultAutoRunButton() {} function setDefaultAutoRunButton() {}
function updateAutoDelayInputState() {}
function updateFallbackThreadIntervalInputState() {} function updateFallbackThreadIntervalInputState() {}
function syncScheduledCountdownTicker() {} function syncAutoRunCountdownTicker() {}
function updateStopButtonState() {} function updateStopButtonState() {}
function getStepStatuses() { return {}; } function getStepStatuses() { return {}; }
function updateConfigMenuControls() {} function updateConfigMenuControls() {}
@@ -73,8 +73,6 @@ const inputAutoSkipFailures = { checked: false };
const inputContributionNickname = { value: 'tester' }; const inputContributionNickname = { value: 'tester' };
const inputContributionQq = { value: '123456' }; const inputContributionQq = { value: '123456' };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' }; const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const btnAutoRun = { disabled: false, innerHTML: '' }; const btnAutoRun = { disabled: false, innerHTML: '' };
const inputRunCount = { disabled: false }; const inputRunCount = { disabled: false };
const selectFlow = { value: latestState.activeFlowId }; const selectFlow = { value: latestState.activeFlowId };
@@ -132,7 +130,6 @@ function shouldWarnAutoRunFallbackRisk() { return false; }
function isAutoRunFallbackRiskPromptDismissed() { return false; } function isAutoRunFallbackRiskPromptDismissed() { return false; }
async function openAutoRunFallbackRiskConfirmModal() { throw new Error('should not be called'); } async function openAutoRunFallbackRiskConfirmModal() { throw new Error('should not be called'); }
function setAutoRunFallbackRiskPromptDismissed() {} function setAutoRunFallbackRiskPromptDismissed() {}
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
async function refreshContributionContentHint() { async function refreshContributionContentHint() {
events.push({ type: 'refresh' }); events.push({ type: 'refresh' });
${refreshImpl ? 'return (' + refreshImpl + ')();' : 'return null;'} ${refreshImpl ? 'return (' + refreshImpl + ')();' : 'return null;'}
@@ -272,8 +269,6 @@ const inputAutoSkipFailures = { checked: false };
const inputContributionNickname = { value: 'tester' }; const inputContributionNickname = { value: 'tester' };
const inputContributionQq = { value: '123456' }; const inputContributionQq = { value: '123456' };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' }; const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const btnAutoRun = { disabled: false, innerHTML: '' }; const btnAutoRun = { disabled: false, innerHTML: '' };
const inputRunCount = { disabled: false, value: '1' }; const inputRunCount = { disabled: false, value: '1' };
const inputPhoneVerificationEnabled = { checked: true }; const inputPhoneVerificationEnabled = { checked: true };
@@ -324,7 +319,6 @@ function shouldWarnAutoRunFallbackRisk() { return false; }
function isAutoRunFallbackRiskPromptDismissed() { return false; } function isAutoRunFallbackRiskPromptDismissed() { return false; }
async function openAutoRunFallbackRiskConfirmModal() { throw new Error('should not be called'); } async function openAutoRunFallbackRiskConfirmModal() { throw new Error('should not be called'); }
function setAutoRunFallbackRiskPromptDismissed() {} function setAutoRunFallbackRiskPromptDismissed() {}
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
async function refreshContributionContentHint() { async function refreshContributionContentHint() {
events.push({ type: 'refresh' }); events.push({ type: 'refresh' });
return null; return null;
@@ -247,8 +247,6 @@ const inputTempEmailReceiveMailbox = { value: 'relay@example.com' };
const inputTempEmailUseRandomSubdomain = { checked: true }; const inputTempEmailUseRandomSubdomain = { checked: true };
const inputAutoSkipFailures = { checked: false }; const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' }; const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
const inputAutoDelayEnabled = { checked: true };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '10' }; const inputAutoStepDelaySeconds = { value: '10' };
const inputOAuthFlowTimeoutEnabled = { checked: true }; const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '6' }; const inputVerificationResendCount = { value: '6' };
@@ -297,7 +295,6 @@ function normalizeLuckmailEmailType(value) { return String(value || '').trim();
function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); } function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); } function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); } function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; } function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
@@ -38,7 +38,12 @@ test('sidepanel html exposes flow selector and kiro source fields', () => {
'id="select-flow"', 'id="select-flow"',
'<option value="grok">Grok</option>', '<option value="grok">Grok</option>',
'id="label-source-selector"', 'id="label-source-selector"',
'id="btn-open-webchat2api-github"',
'id="row-step6-cookie-settings"', 'id="row-step6-cookie-settings"',
'id="row-shared-auto-run"',
'id="row-auto-run-thread-interval"',
'id="row-oauth-callback"',
'id="row-settings-actions"',
'id="row-kiro-rs-url"', 'id="row-kiro-rs-url"',
'id="btn-open-kiro-rs-github"', 'id="btn-open-kiro-rs-github"',
'id="row-kiro-rs-key"', 'id="row-kiro-rs-key"',
@@ -148,6 +153,10 @@ test('sidepanel Kiro GitHub button opens the configured fork', () => {
assert.doesNotMatch(sidepanelSource, /github\.com\/hank9999\/kiro\.rs/); assert.doesNotMatch(sidepanelSource, /github\.com\/hank9999\/kiro\.rs/);
}); });
test('sidepanel webchat2api GitHub button opens the configured repository', () => {
assert.match(sidepanelSource, /openExternalUrl\('https:\/\/github\.com\/zqbxdev\/webchat2api'\)/);
});
test('sidepanel step definitions rerender when active flow changes even if plus/signup settings stay the same', () => { test('sidepanel step definitions rerender when active flow changes even if plus/signup settings stay the same', () => {
const bundle = [ const bundle = [
extractFunction(sidepanelSource, 'normalizeSignupMethod'), extractFunction(sidepanelSource, 'normalizeSignupMethod'),
-7
View File
@@ -132,8 +132,6 @@ const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false }; const inputTempEmailUseRandomSubdomain = { checked: false };
const inputAutoSkipFailures = { checked: false }; const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' }; const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '' }; const inputAutoStepDelaySeconds = { value: '' };
const inputOAuthFlowTimeoutEnabled = { checked: true }; const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '4' }; const inputVerificationResendCount = { value: '4' };
@@ -220,7 +218,6 @@ function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value |
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); } function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); } function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); } function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; } function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
@@ -408,8 +405,6 @@ const selectLuckmailEmailType = { value: 'ms_graph' };
const inputLuckmailDomain = { value: '' }; const inputLuckmailDomain = { value: '' };
const inputAutoSkipFailures = { checked: false }; const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '' }; const inputAutoSkipFailuresThreadIntervalMinutes = { value: '' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '' };
const inputAutoStepDelaySeconds = { value: '' }; const inputAutoStepDelaySeconds = { value: '' };
const inputOAuthFlowTimeoutEnabled = { checked: true }; const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '' }; const inputVerificationResendCount = { value: '' };
@@ -483,7 +478,6 @@ function applyCloudflareTempEmailSettingsState() {}
function renderCloudflareDomainOptions() {} function renderCloudflareDomainOptions() {}
function setCloudflareDomainEditMode() {} function setCloudflareDomainEditMode() {}
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function formatAutoStepDelayInputValue(value) { return value == null ? '' : String(value); } function formatAutoStepDelayInputValue(value) { return value == null ? '' : String(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; } function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizeHeroSmsMaxPriceValue(value = '') { return String(value || '').trim(); } function normalizeHeroSmsMaxPriceValue(value = '') { return String(value || '').trim(); }
@@ -519,7 +513,6 @@ function updateHeroSmsPlatformDisplay() {}
function updatePhoneSmsProviderOrderSummary() {} function updatePhoneSmsProviderOrderSummary() {}
function applyAutoRunStatus() {} function applyAutoRunStatus() {}
function markSettingsDirty() {} function markSettingsDirty() {}
function updateAutoDelayInputState() {}
function updateFallbackThreadIntervalInputState() {} function updateFallbackThreadIntervalInputState() {}
function updateAccountRunHistorySettingsUI() {} function updateAccountRunHistorySettingsUI() {}
function updatePhoneVerificationSettingsUI() {} function updatePhoneVerificationSettingsUI() {}
@@ -203,8 +203,6 @@ const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false }; const inputTempEmailUseRandomSubdomain = { checked: false };
const inputAutoSkipFailures = { checked: false }; const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' }; const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '' }; const inputAutoStepDelaySeconds = { value: '' };
const inputOAuthFlowTimeoutEnabled = { checked: true }; const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '4' }; const inputVerificationResendCount = { value: '4' };
@@ -258,7 +256,6 @@ function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value |
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); } function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); } function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); } function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; } function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
+17 -5
View File
@@ -38,25 +38,37 @@ function extractFunction(name) {
throw new Error(`unterminated ${name}`); throw new Error(`unterminated ${name}`);
} }
test('sidepanel no longer exposes operation delay switch and places step execution range below oauth timeout', () => { test('sidepanel splits shared auto-run controls from openai oauth controls', () => {
assert.doesNotMatch(html, /id="row-operation-delay-settings"/); assert.doesNotMatch(html, /id="row-operation-delay-settings"/);
assert.doesNotMatch(html, /id="input-operation-delay-enabled"/); assert.doesNotMatch(html, /id="input-operation-delay-enabled"/);
assert.doesNotMatch(html, /id="row-auto-delay-settings"/);
assert.doesNotMatch(html, /id="input-auto-delay-enabled"/);
assert.doesNotMatch(html, /id="input-auto-delay-minutes"/);
const step6CookieIndex = html.indexOf('id="row-step6-cookie-settings"'); const step6CookieIndex = html.indexOf('id="row-step6-cookie-settings"');
const autoDelayIndex = html.indexOf('id="row-auto-delay-settings"'); const sharedAutoRunIndex = html.indexOf('id="row-shared-auto-run"');
const threadIntervalIndex = html.indexOf('id="row-auto-run-thread-interval"');
const oauthTimeoutIndex = html.indexOf('id="row-oauth-flow-timeout"'); const oauthTimeoutIndex = html.indexOf('id="row-oauth-flow-timeout"');
const stepRangeIndex = html.indexOf('id="row-step-execution-range"'); const stepRangeIndex = html.indexOf('id="row-step-execution-range"');
const oauthDisplayIndex = html.indexOf('id="row-oauth-display"'); const oauthDisplayIndex = html.indexOf('id="row-oauth-display"');
const oauthCallbackIndex = html.indexOf('id="row-oauth-callback"');
const settingsActionsIndex = html.indexOf('id="row-settings-actions"');
assert.notEqual(step6CookieIndex, -1); assert.notEqual(step6CookieIndex, -1);
assert.notEqual(autoDelayIndex, -1); assert.notEqual(sharedAutoRunIndex, -1);
assert.notEqual(threadIntervalIndex, -1);
assert.notEqual(oauthTimeoutIndex, -1); assert.notEqual(oauthTimeoutIndex, -1);
assert.notEqual(stepRangeIndex, -1); assert.notEqual(stepRangeIndex, -1);
assert.notEqual(oauthDisplayIndex, -1); assert.notEqual(oauthDisplayIndex, -1);
assert.ok(autoDelayIndex > step6CookieIndex, 'startup delay row should render below the openai step6 cookie row'); assert.notEqual(oauthCallbackIndex, -1);
assert.ok(stepRangeIndex > autoDelayIndex, 'step execution range should still remain below the startup delay row'); assert.notEqual(settingsActionsIndex, -1);
assert.ok(sharedAutoRunIndex > step6CookieIndex, 'shared auto-run should render below the openai step6 cookie row');
assert.ok(threadIntervalIndex > sharedAutoRunIndex, 'thread interval should be part of the shared auto-run block');
assert.ok(threadIntervalIndex < oauthTimeoutIndex, 'thread interval should stay outside openai oauth controls');
assert.ok(stepRangeIndex > oauthTimeoutIndex, 'step execution range should render below oauth timeout'); assert.ok(stepRangeIndex > oauthTimeoutIndex, 'step execution range should render below oauth timeout');
assert.ok(stepRangeIndex < oauthDisplayIndex, 'step execution range should stay above oauth runtime display'); assert.ok(stepRangeIndex < oauthDisplayIndex, 'step execution range should stay above oauth runtime display');
assert.ok(oauthCallbackIndex > oauthDisplayIndex, 'openai callback row should follow the oauth display');
assert.ok(settingsActionsIndex > oauthCallbackIndex, 'save settings action should live outside the callback row');
}); });
test('sidepanel operation delay state is always normalized back to enabled', () => { test('sidepanel operation delay state is always normalized back to enabled', () => {
@@ -713,7 +713,6 @@ function setFreePhoneReuseControlsLocked(locked) {
|| !Boolean(inputPhoneVerificationEnabled.checked && phoneVerificationSectionExpanded); || !Boolean(inputPhoneVerificationEnabled.checked && phoneVerificationSectionExpanded);
} }
function isAutoRunLockedPhase() { return false; } function isAutoRunLockedPhase() { return false; }
function isAutoRunScheduledPhase() { return false; }
${extractFunction('normalizeSignupMethod')} ${extractFunction('normalizeSignupMethod')}
${extractFunction('normalizeHeroSmsReuseEnabledValue')} ${extractFunction('normalizeHeroSmsReuseEnabledValue')}
@@ -979,8 +978,6 @@ const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false }; const inputTempEmailUseRandomSubdomain = { checked: false };
const inputAutoSkipFailures = { checked: false }; const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' }; const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '' }; const inputAutoStepDelaySeconds = { value: '' };
const inputPhoneVerificationEnabled = { checked: true }; const inputPhoneVerificationEnabled = { checked: true };
const inputFreePhoneReuseEnabled = { checked: true }; const inputFreePhoneReuseEnabled = { checked: true };
@@ -1072,7 +1069,6 @@ function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value |
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); } function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); } function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); } function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; } function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
-4
View File
@@ -76,7 +76,6 @@ let autoRunCurrentRun = 2;
let autoRunTotalRuns = 3; let autoRunTotalRuns = 3;
let autoRunAttemptRun = 4; let autoRunAttemptRun = 4;
let autoRunSessionId = 99; let autoRunSessionId = 99;
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
const DEFAULT_STATE = { const DEFAULT_STATE = {
nodeStatuses: {}, nodeStatuses: {},
}; };
@@ -158,9 +157,6 @@ async function getState() {
function getPendingAutoRunTimerPlan() { function getPendingAutoRunTimerPlan() {
return null; return null;
} }
function isAutoRunScheduledState() {
return false;
}
function getStep8CallbackUrlFromNavigation() { return ''; } function getStep8CallbackUrlFromNavigation() { return ''; }
function getStep8CallbackUrlFromTabUpdate() { return ''; } function getStep8CallbackUrlFromTabUpdate() { return ''; }
function getStep8EffectLabel() { return 'no_effect'; } function getStep8EffectLabel() { return 'no_effect'; }