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
+5 -231
View File
@@ -638,14 +638,11 @@ const IP_PROXY_TARGET_HOST_PATTERNS = [
];
const AUTO_RUN_TIMER_ALARM_NAME = 'auto-run-timer';
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_BEFORE_RETRY = 'before_retry';
const IP_PROXY_AUTO_SYNC_INTERVAL_MIN_MINUTES = 1;
const IP_PROXY_AUTO_SYNC_INTERVAL_MAX_MINUTES = 1440;
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_MAX_RETRIES_PER_ROUND = 3;
const AUTO_STEP_DELAY_MIN_ALLOWED_SECONDS = 0;
@@ -1354,9 +1351,7 @@ const PERSISTED_SETTING_DEFAULTS = {
autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0,
oauthFlowTimeoutEnabled: true,
autoRunDelayEnabled: false,
operationDelayEnabled: true,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
step6CookieCleanupEnabled: false,
stepExecutionRangeByFlow: {},
@@ -1530,7 +1525,6 @@ const DEFAULT_STATE = {
autoRunAttemptRun: 0, // 当前轮次的重试序号。
autoRunSessionId: 0,
autoRunRoundSummaries: [], // 自动运行轮次摘要。
scheduledAutoRunAt: null, // 自动运行计划启动时间戳。
autoRunTimerPlan: null, // 自动运行可恢复计时计划快照。
autoRunCountdownAt: null,
autoRunCountdownTitle: '',
@@ -1560,17 +1554,6 @@ const DEFAULT_STATE = {
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) {
const rawValue = String(value ?? '').trim();
if (!rawValue) {
@@ -1583,7 +1566,7 @@ function normalizeAutoRunFallbackThreadIntervalMinutes(value) {
}
return Math.min(
AUTO_RUN_DELAY_MAX_MINUTES,
1440,
Math.max(0, Math.floor(numeric))
);
}
@@ -2009,7 +1992,6 @@ function isPhoneSignupIdentityStateForReuse(state = {}) {
const runtimeActive = (
(typeof isAutoRunLockedState === 'function' && isAutoRunLockedState(state))
|| (typeof isAutoRunPausedState === 'function' && isAutoRunPausedState(state))
|| (typeof isAutoRunScheduledState === 'function' && isAutoRunScheduledState(state))
|| Boolean(state?.autoRunning)
);
if (!runtimeActive) {
@@ -2311,9 +2293,6 @@ function normalizeRunCount(value) {
function normalizeAutoRunTimerKind(value = '') {
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) {
return AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS;
}
@@ -2393,22 +2372,6 @@ function normalizeAutoRunTimerPlan(plan) {
const countdownTitle = String(plan.countdownTitle || '').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) {
const normalizedCurrentRun = Math.max(1, Math.min(totalRuns, currentRun));
const normalizedAttemptRun = Math.max(1, attemptRun);
@@ -2449,28 +2412,11 @@ function normalizeAutoRunTimerPlanFromState(state = {}) {
if (directPlan) {
return directPlan;
}
if (state.autoRunPhase !== 'scheduled') {
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,
});
return null;
}
function getAutoRunTimerPlanPhase(kind = '') {
return kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START ? 'scheduled' : 'waiting_interval';
return 'waiting_interval';
}
function getAutoRunTimerStatusPayload(plan) {
@@ -2486,7 +2432,6 @@ function getAutoRunTimerStatusPayload(plan) {
totalRuns: normalizedPlan.totalRuns,
attemptRun: normalizedPlan.attemptRun,
sessionId: normalizedPlan.autoRunSessionId,
scheduledAt: phase === 'scheduled' ? normalizedPlan.fireAt : null,
countdownAt: normalizedPlan.fireAt,
countdownTitle: normalizedPlan.countdownTitle,
countdownNote: normalizedPlan.countdownNote,
@@ -3387,7 +3332,6 @@ function normalizePersistentSettingValue(key, value) {
case 'oauthFlowTimeoutEnabled':
case 'gopayHelperLocalSmsHelperEnabled':
case 'gopayHelperAutoModeEnabled':
case 'autoRunDelayEnabled':
return Boolean(value);
case 'operationDelayEnabled':
return true;
@@ -3408,8 +3352,6 @@ function normalizePersistentSettingValue(key, value) {
return normalizePhoneSmsProviderOrder(value);
case 'autoRunFallbackThreadIntervalMinutes':
return normalizeAutoRunFallbackThreadIntervalMinutes(value);
case 'autoRunDelayMinutes':
return normalizeAutoRunDelayMinutes(value);
case 'autoStepDelaySeconds':
return normalizeAutoStepDelaySeconds(value, PERSISTED_SETTING_DEFAULTS.autoStepDelaySeconds);
case 'verificationResendCount':
@@ -10090,8 +10032,7 @@ function getAutoRunStatusPayload(phase, payload = {}) {
return loggingStatus.getAutoRunStatusPayload(phase, normalizedPayload);
}
return {
autoRunning: phase === 'scheduled'
|| phase === 'running'
autoRunning: phase === 'running'
|| phase === 'waiting_step'
|| phase === 'waiting_email'
|| phase === 'retrying'
@@ -10101,7 +10042,6 @@ function getAutoRunStatusPayload(phase, payload = {}) {
autoRunTotalRuns: normalizedPayload.totalRuns ?? 1,
autoRunAttemptRun: normalizedPayload.attemptRun ?? 0,
autoRunSessionId: normalizeAutoRunSessionId(normalizedPayload.sessionId),
scheduledAutoRunAt: Number.isFinite(Number(normalizedPayload.scheduledAt)) ? Number(normalizedPayload.scheduledAt) : null,
autoRunCountdownAt: Number.isFinite(Number(normalizedPayload.countdownAt)) ? Number(normalizedPayload.countdownAt) : null,
autoRunCountdownTitle: normalizedPayload.countdownTitle === undefined ? '' : String(normalizedPayload.countdownTitle || ''),
autoRunCountdownNote: normalizedPayload.countdownNote === undefined ? '' : String(normalizedPayload.countdownNote || ''),
@@ -10109,9 +10049,6 @@ function getAutoRunStatusPayload(phase, payload = {}) {
}
async function broadcastAutoRunStatus(phase, payload = {}, extraState = {}) {
const rawScheduledAt = phase === 'scheduled'
? (payload.scheduledAt ?? payload.scheduledAutoRunAt ?? null)
: null;
const rawCountdownAt = payload.countdownAt ?? payload.autoRunCountdownAt ?? null;
const statusPayload = {
phase,
@@ -10119,7 +10056,6 @@ async function broadcastAutoRunStatus(phase, payload = {}, extraState = {}) {
totalRuns: payload.totalRuns ?? autoRunTotalRuns,
attemptRun: payload.attemptRun ?? autoRunAttemptRun,
sessionId: payload.sessionId ?? payload.autoRunSessionId ?? autoRunSessionId,
scheduledAt: rawScheduledAt === null ? null : Number(rawScheduledAt),
countdownAt: rawCountdownAt === null ? null : Number(rawCountdownAt),
countdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
countdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''),
@@ -10149,38 +10085,10 @@ function isAutoRunPausedState(state) {
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 = {}) {
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) {
if (!Number.isFinite(fireAt) || fireAt <= Date.now()) {
return false;
@@ -10212,7 +10120,6 @@ async function persistAutoRunTimerPlan(plan, extraState = {}) {
{
...extraState,
autoRunTimerPlan: normalizedPlan,
scheduledAutoRunPlan: null,
}
);
await ensureAutoRunTimerAlarm(normalizedPlan.fireAt);
@@ -10225,22 +10132,6 @@ function getAutoRunTimerResumeOptions(plan) {
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) {
const nextRun = Math.min(normalizedPlan.currentRun + 1, normalizedPlan.totalRuns);
return {
@@ -10314,35 +10205,10 @@ async function launchAutoRunTimerPlan(trigger = 'alarm', options = {}) {
}, {
autoRunRoundSummaries: [],
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
});
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();
if (plan.autoRunSessionId && !isCurrentAutoRunSessionId(plan.autoRunSessionId)) {
return false;
@@ -10351,9 +10217,6 @@ async function launchAutoRunTimerPlan(trigger = 'alarm', options = {}) {
autoRunTotalRuns = plan.totalRuns;
autoRunAttemptRun = resumeOptions.statusPayload.attemptRun;
autoRunSessionId = normalizeAutoRunSessionId(plan.autoRunSessionId);
if (plan.kind === AUTO_RUN_TIMER_KIND_SCHEDULED_START && trigger !== 'manual' && state.autoRunDelayEnabled) {
await setAutoRunDelayEnabledState(false);
}
await broadcastAutoRunStatus(
'running',
resumeOptions.statusPayload,
@@ -10361,7 +10224,6 @@ async function launchAutoRunTimerPlan(trigger = 'alarm', options = {}) {
autoRunSkipFailures: plan.autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(plan.totalRuns, plan.roundSummaries),
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() {
const state = await getState();
let plan = getPendingAutoRunTimerPlan(state);
if (!plan) {
clearCurrentAutoRunSessionId();
if (state.autoRunPhase === 'scheduled' || state.autoRunPhase === 'waiting_interval') {
if (state.autoRunPhase === 'waiting_interval') {
await clearAutoRunTimerAlarm();
await broadcastAutoRunStatus('idle', {
currentRun: 0,
@@ -10478,7 +10271,6 @@ async function restoreAutoRunTimerIfNeeded() {
autoRunSessionId: 0,
autoRunRoundSummaries: [],
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
});
}
return;
@@ -10511,7 +10303,6 @@ async function restoreAutoRunTimerIfNeeded() {
autoRunSkipFailures: plan.autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(plan.totalRuns, plan.roundSummaries),
autoRunTimerPlan: plan,
scheduledAutoRunPlan: null,
}
);
await ensureAutoRunTimerAlarm(plan.fireAt);
@@ -10526,9 +10317,6 @@ async function ensureManualInteractionAllowed(actionLabel) {
if (isAutoRunPausedState(state)) {
throw new Error(`自动流程当前已暂停。请点击“继续”,或先确认接管自动流程后再${actionLabel}`);
}
if (isAutoRunScheduledState(state)) {
throw new Error(`自动流程已计划启动。请先取消计划,或立即开始后再${actionLabel}`);
}
return state;
}
@@ -11495,15 +11283,6 @@ async function requestStop(options = {}) {
const inferredStopNode = inferStoppedRecordNode(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) {
autoRunCurrentRun = timerPlan.currentRun;
autoRunTotalRuns = timerPlan.totalRuns;
@@ -11522,7 +11301,6 @@ async function requestStop(options = {}) {
autoRunSkipFailures: timerPlan.autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(timerPlan.totalRuns, timerPlan.roundSummaries),
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
});
await clearAutoRunTimerAlarm();
clearStopRequest();
@@ -11582,7 +11360,6 @@ async function requestStop(options = {}) {
}, {
autoRunSessionId: 0,
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
});
}
@@ -14153,7 +13930,6 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
buildPersistentSettingsPayload,
broadcastDataUpdate,
applyIpProxySettingsFromState,
cancelScheduledAutoRun,
checkIcloudSession,
clearAccountRunHistory: (...args) => clearAndBroadcastAccountRunHistory(...args),
deleteAccountRunHistoryRecords: (...args) => deleteAndBroadcastAccountRunHistoryRecords(...args),
@@ -14260,7 +14036,6 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
normalizeMail2925Accounts,
normalizePayPalAccounts,
normalizeRunCount,
AUTO_RUN_TIMER_KIND_SCHEDULED_START,
notifyNodeComplete,
notifyNodeError,
patchHotmailAccount,
@@ -14270,7 +14045,6 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
probeIpProxyExit,
resetState,
resumeAutoRun,
scheduleAutoRun,
selectLuckmailPurchase,
switchIpProxy,
changeIpProxyExit,
-4
View File
@@ -158,8 +158,6 @@
kiroRsKey: state.kiroRsKey,
autoRunSkipFailures: state.autoRunSkipFailures,
autoRunFallbackThreadIntervalMinutes: state.autoRunFallbackThreadIntervalMinutes,
autoRunDelayEnabled: state.autoRunDelayEnabled,
autoRunDelayMinutes: state.autoRunDelayMinutes,
autoStepDelaySeconds: state.autoStepDelaySeconds,
stepExecutionRangeByFlow: state.stepExecutionRangeByFlow,
signupMethod: state.signupMethod,
@@ -502,7 +500,6 @@
}, {
autoRunSessionId: 0,
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
});
clearStopRequest();
}
@@ -1159,7 +1156,6 @@
autoRunSessionId: 0,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
...getAutoRunStatusPayload(deps.getStopRequested() || stoppedEarly ? 'stopped' : 'complete', {
currentRun: deps.getStopRequested() || stoppedEarly ? afterRuntime.autoRunCurrentRun : afterRuntime.autoRunTotalRuns,
totalRuns: afterRuntime.autoRunTotalRuns,
+1 -3
View File
@@ -209,8 +209,7 @@
function getAutoRunStatusPayload(phase, payload = {}) {
return {
autoRunning: phase === 'scheduled'
|| phase === 'running'
autoRunning: phase === 'running'
|| phase === 'waiting_step'
|| phase === 'waiting_email'
|| phase === 'retrying'
@@ -220,7 +219,6 @@
autoRunTotalRuns: payload.totalRuns ?? 1,
autoRunAttemptRun: payload.attemptRun ?? 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,
autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
autoRunCountdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''),
+1 -66
View File
@@ -11,7 +11,6 @@
buildPersistentSettingsPayload,
broadcastDataUpdate,
applyIpProxySettingsFromState,
cancelScheduledAutoRun,
checkIcloudSession,
clearAccountRunHistory,
deleteAccountRunHistoryRecords,
@@ -145,7 +144,6 @@
normalizeMail2925Accounts,
normalizePayPalAccounts,
normalizeRunCount,
AUTO_RUN_TIMER_KIND_SCHEDULED_START,
notifyNodeComplete,
notifyNodeError,
patchMail2925Account,
@@ -158,7 +156,6 @@
handleCloudflareSecurityBlocked,
resetState,
resumeAutoRun,
scheduleAutoRun,
selectLuckmailPurchase,
switchIpProxy,
changeIpProxyExit,
@@ -1324,7 +1321,7 @@
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
}
if (getPendingAutoRunTimerPlan(state)) {
throw new Error('已有自动运行倒计时计划,请先取消或立即开始。');
throw new Error('已有线程间隔等待,请先停止或立即继续。');
}
const totalRuns = normalizeRunCount(message.payload?.totalRuns || 1);
const autoRunSkipFailures = Boolean(message.payload?.autoRunSkipFailures);
@@ -1334,68 +1331,6 @@
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': {
clearStopRequest();
if (message.source === 'sidepanel') {
+14
View File
@@ -49,6 +49,20 @@
label: 'IP \u4ee3\u7406',
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() {
+1 -3
View File
@@ -209,8 +209,7 @@
function getAutoRunStatusPayload(phase, payload = {}) {
return {
autoRunning: phase === 'scheduled'
|| phase === 'running'
autoRunning: phase === 'running'
|| phase === 'waiting_step'
|| phase === 'waiting_email'
|| phase === 'retrying'
@@ -220,7 +219,6 @@
autoRunTotalRuns: payload.totalRuns ?? 1,
autoRunAttemptRun: payload.attemptRun ?? 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,
autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
autoRunCountdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''),
+44 -70
View File
@@ -115,48 +115,11 @@
if (isPlainObject(range)) {
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);
return { enabled: false, fromStep: 1, toStep: lastStep };
}
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 flowDefaults = getFlowSettingsDefaults(flowId);
const targetDefaults = isPlainObject(flowDefaults?.targets?.[targetId])
@@ -200,24 +163,7 @@
stepExecutionRange: getDefaultStepExecutionRange(flowId),
},
};
if (flowId === 'openai') {
return mergePlainObjects(base, {
signup: {
signupMethod: 'email',
phoneVerificationEnabled: false,
phoneSignupReloginAfterBindEmailEnabled: false,
},
plus: {
plusModeEnabled: false,
plusPaymentMethod: 'paypal-hosted',
plusAccountAccessStrategy: 'oauth',
hostedCheckoutVerificationUrl: '',
hostedCheckoutPhoneNumber: '',
plusHostedCheckoutOauthDelaySeconds: 3,
},
});
}
return base;
return mergePlainObjects(base, getFlowSettingsDefaults(flowId));
}
function buildDefaultFlows() {
@@ -364,6 +310,18 @@
}
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 = {
...currentFlow.targets.cpa,
...getTargetValue(
@@ -407,66 +365,82 @@
...currentFlow,
targets: {
...currentFlow.targets,
cpa: normalizeFlowTargetState('openai', 'cpa', cpaSource, defaults.flows.openai.targets.cpa),
sub2api: normalizeFlowTargetState('openai', 'sub2api', sub2apiSource, defaults.flows.openai.targets.sub2api),
codex2api: normalizeFlowTargetState('openai', 'codex2api', codex2apiSource, defaults.flows.openai.targets.codex2api),
cpa: normalizeFlowTargetState('openai', 'cpa', cpaSource, defaultOpenAiTargets.cpa || {}),
sub2api: normalizeFlowTargetState('openai', 'sub2api', sub2apiSource, defaultOpenAiTargets.sub2api || {}),
codex2api: normalizeFlowTargetState('openai', 'codex2api', codex2apiSource, defaultOpenAiTargets.codex2api || {}),
},
signup: {
signupMethod: String(
input?.signupMethod
?? currentFlow.signup?.signupMethod
?? defaults.flows.openai.signup.signupMethod
?? defaultOpenAiSignup.signupMethod
?? 'email'
).trim().toLowerCase() === 'phone' ? 'phone' : 'email',
phoneVerificationEnabled: Boolean(
input?.phoneVerificationEnabled
?? currentFlow.signup?.phoneVerificationEnabled
?? defaults.flows.openai.signup.phoneVerificationEnabled
?? defaultOpenAiSignup.phoneVerificationEnabled
?? false
),
phoneSignupReloginAfterBindEmailEnabled: Boolean(
input?.phoneSignupReloginAfterBindEmailEnabled
?? currentFlow.signup?.phoneSignupReloginAfterBindEmailEnabled
?? defaults.flows.openai.signup.phoneSignupReloginAfterBindEmailEnabled
?? defaultOpenAiSignup.phoneSignupReloginAfterBindEmailEnabled
?? false
),
},
plus: {
plusModeEnabled: Boolean(
input?.plusModeEnabled
?? currentFlow.plus?.plusModeEnabled
?? defaults.flows.openai.plus.plusModeEnabled
?? defaultOpenAiPlus.plusModeEnabled
?? false
),
plusPaymentMethod: String(
input?.plusPaymentMethod
?? currentFlow.plus?.plusPaymentMethod
?? defaults.flows.openai.plus.plusPaymentMethod
).trim() || defaults.flows.openai.plus.plusPaymentMethod,
?? defaultOpenAiPlus.plusPaymentMethod
?? 'paypal-hosted'
).trim() || defaultOpenAiPlus.plusPaymentMethod || 'paypal-hosted',
plusAccountAccessStrategy: normalizePlusAccountAccessStrategy(
input?.plusAccountAccessStrategy
?? currentFlow.plus?.plusAccountAccessStrategy
?? defaults.flows.openai.plus.plusAccountAccessStrategy
?? defaultOpenAiPlus.plusAccountAccessStrategy
?? 'oauth'
),
hostedCheckoutVerificationUrl: String(
input?.hostedCheckoutVerificationUrl
?? currentFlow.plus?.hostedCheckoutVerificationUrl
?? defaults.flows.openai.plus.hostedCheckoutVerificationUrl
?? defaultOpenAiPlus.hostedCheckoutVerificationUrl
?? ''
).trim(),
hostedCheckoutPhoneNumber: String(
input?.hostedCheckoutPhoneNumber
?? currentFlow.plus?.hostedCheckoutPhoneNumber
?? defaults.flows.openai.plus.hostedCheckoutPhoneNumber
?? defaultOpenAiPlus.hostedCheckoutPhoneNumber
?? ''
).trim(),
plusHostedCheckoutOauthDelaySeconds: (() => {
const numeric = Number(
input?.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 = {}) {
const defaultKiroFlow = isPlainObject(defaults?.flows?.kiro)
? defaults.flows.kiro
: {};
const defaultKiroTargets = isPlainObject(defaultKiroFlow.targets)
? defaultKiroFlow.targets
: {};
const targetSource = {
...currentFlow.targets['kiro-rs'],
baseUrl: input?.kiroRsUrl ?? input?.kiroRsBaseUrl ?? currentFlow.targets['kiro-rs'].baseUrl,
@@ -476,7 +450,7 @@
...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_REGISTER_PAGE_SOURCE_ID = 'grok-register-page';
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_PRE_SSO_EXTRACT_WAIT_MS = 10 * 1000;
const MAIL_2925_FILTER_LOOKBACK_MS = 10 * 60 * 1000;
@@ -191,6 +193,50 @@
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 = {}) {
const domain = cleanString(cookie.domain).replace(/^\.+/, '').toLowerCase();
return GROK_COOKIE_CLEAR_DOMAINS.some((target) => (
@@ -395,8 +441,12 @@
});
const result = await sendGrokCommand(nodeId, { email }, {
step: 2,
timeoutMs: GROK_VERIFICATION_READY_TIMEOUT_MS + 15000,
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 completeNode(nodeId, {
grokEmail: email,
@@ -456,6 +506,23 @@
|| currentState.runtimeState?.flowState?.grok?.register?.email
|| currentState.email
).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({
actionLabel: 'Grok 验证码',
filterAfterTimestamp,
@@ -478,7 +545,6 @@
if (!code) {
throw new Error('未能获取到 xAI 邮箱验证码。');
}
const tabId = await ensureGrokRegisterTab(currentState, { openIfMissing: false });
await activateTab(tabId);
await ensureContentReady(tabId);
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_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_EMAIL_VERIFICATION_READY_TIMEOUT_MS = 90 * 1000;
const GROK_PROFILE_SUBMIT_PRE_CLICK_DELAY_MS = 2000;
function isVisibleGrokElement(element) {
@@ -174,6 +175,29 @@ function getGrokEmailErrorText() {
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 = {}) {
const email = String(payload.email || '').trim();
if (!email) throw new Error('缺少 Grok 注册邮箱。');
@@ -189,7 +213,8 @@ async function submitGrokEmail(payload = {}) {
if (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() {
+1 -1
View File
@@ -35,7 +35,7 @@
stepDefinitionMode: 'grok',
targetSelectorLabel: '来源',
},
baseGroups: ['grok-runtime-status'],
baseGroups: ['grok-runtime-status', 'shared-auto-run', 'shared-settings-actions'],
targets: {
webchat2api: {
id: 'webchat2api',
+7 -1
View File
@@ -40,12 +40,18 @@
"targetSelectorLabel": "来源"
},
"baseGroups": [
"kiro-runtime-status"
"kiro-runtime-status",
"shared-auto-run",
"shared-settings-actions"
],
"targets": {
"kiro-rs": {
"id": "kiro-rs",
"label": "kiro.rs",
"defaultState": {
"baseUrl": "",
"apiKey": ""
},
"groups": [
"kiro-target-kiro-rs"
]
+48 -2
View File
@@ -46,13 +46,20 @@
"baseGroups": [
"openai-plus",
"openai-phone",
"shared-auto-run",
"openai-oauth",
"openai-step6"
"openai-step6",
"shared-settings-actions"
],
"targets": {
"cpa": {
"id": "cpa",
"label": "CPA 面板",
"defaultState": {
"vpsUrl": "",
"vpsPassword": "",
"localCpaStep9Mode": "submit"
},
"groups": [
"openai-target-cpa"
]
@@ -60,6 +67,18 @@
"sub2api": {
"id": "sub2api",
"label": "SUB2API",
"defaultState": {
"sub2apiUrl": "",
"sub2apiEmail": "",
"sub2apiPassword": "",
"sub2apiGroupName": "codex",
"sub2apiGroupNames": [
"codex",
"openai-plus"
],
"sub2apiAccountPriority": 1,
"sub2apiDefaultProxyName": ""
},
"groups": [
"openai-target-sub2api"
]
@@ -67,11 +86,37 @@
"codex2api": {
"id": "codex2api",
"label": "Codex2API",
"defaultState": {
"codex2apiUrl": "",
"codex2apiAdminKey": ""
},
"groups": [
"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": {
"openai-auth": {
"flowId": "openai",
@@ -383,7 +428,8 @@
"label": "OAuth",
"rowIds": [
"row-oauth-flow-timeout",
"row-oauth-display"
"row-oauth-display",
"row-oauth-callback"
]
},
"openai-step6": {
+1 -1
View File
@@ -1263,7 +1263,7 @@ function updateIpProxyUI(state = latestState) {
const isAccountMode = mode === 'account';
const showSessionOptions = isAccountMode && service === '711proxy';
const hasAccountListConfigured = accountListAvailable && isAccountMode && hasCurrentInputAccountListEntries();
const canOperate = !isAutoRunLockedPhase() && !isAutoRunScheduledPhase();
const canOperate = !isAutoRunLockedPhase();
const actionState = getIpProxyActionState();
const actionBusy = Boolean(actionState.busy);
const busyAction = normalizeIpProxyActionType(actionState.action);
+19 -23
View File
@@ -1268,6 +1268,19 @@ header {
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 {
font-size: 10px;
letter-spacing: 0.02em;
@@ -2197,15 +2210,6 @@ header {
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 {
gap: 8px;
}
@@ -2227,10 +2231,6 @@ header {
text-align: center;
}
.auto-run-delay-setting {
margin-left: auto !important;
}
.oauth-flow-timeout-setting {
gap: 8px;
min-width: 0;
@@ -2242,10 +2242,6 @@ header {
text-overflow: ellipsis;
}
#row-auto-delay-settings .setting-caption {
min-width: auto;
}
.setting-caption-left {
text-align: left;
}
@@ -2720,7 +2716,7 @@ header {
.auto-continue-bar svg { color: var(--orange); flex-shrink: 0; }
.auto-hint { font-size: 13px; color: var(--orange); flex: 1; font-weight: 500; }
.auto-schedule-bar {
.auto-countdown-bar {
margin-top: 8px;
padding: 10px;
background: var(--blue-soft);
@@ -2732,12 +2728,12 @@ header {
gap: 10px;
}
.auto-schedule-bar svg {
.auto-countdown-bar svg {
color: var(--blue);
flex-shrink: 0;
}
.auto-schedule-copy {
.auto-countdown-copy {
display: flex;
flex-direction: column;
gap: 2px;
@@ -2745,19 +2741,19 @@ header {
min-width: 0;
}
.auto-schedule-title {
.auto-countdown-title {
font-size: 13px;
font-weight: 700;
color: var(--blue);
}
.auto-schedule-meta {
.auto-countdown-meta {
font-size: 12px;
line-height: 1.5;
color: var(--text-secondary);
}
.auto-schedule-actions {
.auto-countdown-actions {
display: flex;
align-items: center;
flex-wrap: wrap;
+45 -54
View File
@@ -121,7 +121,7 @@
<section id="data-section">
<div id="settings-card" class="data-card">
<div class="data-row">
<div class="data-row" id="row-flow-selector">
<span class="data-label">注册</span>
<select id="select-flow" class="data-select" aria-label="Flow 选择">
<option value="openai" selected>Codex / OpenAI</option>
@@ -129,13 +129,16 @@
<option value="grok">Grok</option>
</select>
</div>
<div class="data-row">
<div class="data-row" id="row-source-selector">
<span id="label-source-selector" class="data-label">来源</span>
<select id="select-panel-mode" class="data-select">
<option value="cpa">CPA 面板</option>
<option value="sub2api">SUB2API</option>
<option value="codex2api">Codex2API</option>
</select>
<div class="data-inline">
<select id="select-panel-mode" class="data-select">
<option value="cpa">CPA 面板</option>
<option value="sub2api">SUB2API</option>
<option value="codex2api">Codex2API</option>
</select>
<button id="btn-open-webchat2api-github" class="btn btn-ghost btn-xs data-inline-btn" type="button">GitHub</button>
</div>
</div>
<div id="contribution-mode-panel" class="contribution-mode-panel" hidden>
<div class="contribution-mode-panel-header">
@@ -291,20 +294,8 @@
<span class="data-label">上传状态</span>
<span id="display-kiro-upload-status" class="data-value mono">未开始</span>
</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;">
<span class="data-label">webchat2api</span>
<span class="data-label">webchat</span>
<input type="text" id="input-grok-webchat2api-url" class="data-input"
placeholder="请输入 webchat2api 地址,例如 http://localhost:83" />
</div>
@@ -319,6 +310,18 @@
title="显示 webchat2api 管理密钥"></button>
</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;">
<span class="data-label">SSO Cookie</span>
<div class="data-inline">
@@ -694,26 +697,7 @@
</div>
</div>
</div>
<div class="data-row module-divider-start" id="row-auto-delay-settings">
<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">
<div class="data-row module-divider-start" id="row-shared-auto-run">
<span class="data-label">自动重试</span>
<div class="data-inline setting-pair">
<div class="setting-group setting-group-primary">
@@ -734,6 +718,16 @@
</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">
<span class="data-label">授权总超时</span>
<div class="data-inline setting-pair">
@@ -748,14 +742,6 @@
</label>
<span class="setting-caption oauth-flow-timeout-caption">关闭后只取消 Step 7 后链总预算</span>
</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 class="data-row" id="row-step-execution-range">
@@ -782,10 +768,15 @@
<span class="data-label">OAuth</span>
<span id="display-oauth-url" class="data-value mono truncate">等待中...</span>
</div>
<div class="data-row">
<div class="data-row" id="row-oauth-callback">
<span class="data-label">回调</span>
<div class="data-inline data-value-actions">
<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>
</div>
</div>
@@ -1798,16 +1789,16 @@
<span class="auto-hint">先自动获取邮箱,或手动粘贴邮箱后再继续</span>
<button id="btn-auto-continue" class="btn btn-primary btn-sm">继续</button>
</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">
<circle cx="12" cy="12" r="10" />
<polyline points="12 6 12 12 16 14" />
</svg>
<div class="auto-schedule-copy">
<span id="auto-schedule-title" class="auto-schedule-title">已计划自动运行</span>
<span id="auto-schedule-meta" class="auto-schedule-meta">等待倒计时开始...</span>
<div class="auto-countdown-copy">
<span id="auto-countdown-title" class="auto-countdown-title">等待继续</span>
<span id="auto-countdown-meta" class="auto-countdown-meta">等待倒计时开始...</span>
</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-cancel-schedule" class="btn btn-outline btn-sm" type="button">取消</button>
</div>
+51 -160
View File
@@ -77,11 +77,10 @@ const stepsProgress = document.getElementById('steps-progress');
const btnAutoRun = document.getElementById('btn-auto-run');
const btnAutoContinue = document.getElementById('btn-auto-continue');
const autoContinueBar = document.getElementById('auto-continue-bar');
const autoScheduleBar = document.getElementById('auto-schedule-bar');
const autoScheduleTitle = document.getElementById('auto-schedule-title');
const autoScheduleMeta = document.getElementById('auto-schedule-meta');
const autoCountdownBar = document.getElementById('auto-countdown-bar');
const autoCountdownTitle = document.getElementById('auto-countdown-title');
const autoCountdownMeta = document.getElementById('auto-countdown-meta');
const btnAutoRunNow = document.getElementById('btn-auto-run-now');
const btnAutoCancelSchedule = document.getElementById('btn-auto-cancel-schedule');
const btnClearLog = document.getElementById('btn-clear-log');
const configMenuShell = document.getElementById('config-menu-shell');
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 labelSourceSelector = document.getElementById('label-source-selector');
const selectPanelMode = document.getElementById('select-panel-mode');
const btnOpenWebchat2ApiGithub = document.getElementById('btn-open-webchat2api-github');
const rowVpsUrl = document.getElementById('row-vps-url');
const inputVpsUrl = document.getElementById('input-vps-url');
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 inputAutoSkipFailuresThreadIntervalMinutes = document.getElementById('input-auto-skip-failures-thread-interval-minutes');
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 inputOAuthFlowTimeoutEnabled = document.getElementById('input-oauth-flow-timeout-enabled');
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_DEFAULT_STATUSES = Object.fromEntries(NODE_IDS.map((nodeId) => [nodeId, 'pending']));
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_MAX_MINUTES = 1440;
const AUTO_FALLBACK_THREAD_INTERVAL_DEFAULT_MINUTES = 0;
@@ -1365,7 +1360,6 @@ let currentAutoRun = {
currentRun: 0,
totalRuns: 1,
attemptRun: 0,
scheduledAt: null,
countdownAt: null,
countdownTitle: '',
countdownNote: '',
@@ -1386,7 +1380,7 @@ let currentModalActions = [];
let modalResultBuilder = null;
let activePlusManualConfirmationRequestId = '';
let plusManualConfirmationDialogInFlight = false;
let scheduledCountdownTimer = null;
let autoRunCountdownTimer = null;
let configMenuOpen = false;
let configActionInFlight = false;
let currentReleaseSnapshot = null;
@@ -1426,9 +1420,7 @@ function shouldAttachAutomationWindow(message = {}) {
return [
'EXECUTE_NODE',
'AUTO_RUN',
'SCHEDULE_AUTO_RUN',
'RESUME_AUTO_RUN',
'START_SCHEDULED_AUTO_RUN_NOW',
'SKIP_AUTO_RUN_COUNTDOWN',
'PROBE_IP_PROXY_EXIT',
].includes(String(message?.type || '').trim());
@@ -1680,7 +1672,6 @@ const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = Object.fromEntries(
const getIcloudLoginUrlForHost = window.IcloudUtils?.getIcloudLoginUrlForHost
|| ((host) => host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : (host === 'icloud.com' ? 'https://www.icloud.com/' : ''));
btnAutoCancelSchedule?.remove();
const MAIL_PROVIDER_LOGIN_CONFIGS = {
[ICLOUD_PROVIDER]: {
label: 'iCloud 邮箱',
@@ -2982,7 +2973,7 @@ function applyStepExecutionRangeState(state = latestState) {
if (inputStepExecutionRangeEnabled) {
inputStepExecutionRangeEnabled.checked = Boolean(range.enabled);
}
const controlsDisabled = !available || isAutoRunLockedPhase() || isAutoRunScheduledPhase();
const controlsDisabled = !available || isAutoRunLockedPhase();
if (inputStepExecutionRangeEnabled) inputStepExecutionRangeEnabled.disabled = controlsDisabled;
if (inputStepExecutionRangeFrom) inputStepExecutionRangeFrom.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked;
if (inputStepExecutionRangeTo) inputStepExecutionRangeTo.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked;
@@ -3083,7 +3074,7 @@ function hasSavedProgress(state = latestState) {
function isContributionModeSwitchBlocked(state = latestState) {
const statuses = getStepStatuses(state);
const anyRunning = Object.values(statuses).some((status) => status === 'running');
return anyRunning || isAutoRunLockedPhase() || isAutoRunPausedPhase() || isAutoRunScheduledPhase();
return anyRunning || isAutoRunLockedPhase() || isAutoRunPausedPhase();
}
function shouldOfferAutoModeChoice(state = latestState) {
@@ -3630,7 +3621,7 @@ function syncAutoRunState(source = {}) {
const autoRunning = source.autoRunning !== undefined
? Boolean(source.autoRunning)
: (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 = {
@@ -3639,7 +3630,6 @@ function syncAutoRunState(source = {}) {
currentRun: readAutoRunStateValue(source, ['autoRunCurrentRun', 'currentRun'], currentAutoRun.currentRun),
totalRuns: readAutoRunStateValue(source, ['autoRunTotalRuns', 'totalRuns'], currentAutoRun.totalRuns),
attemptRun: readAutoRunStateValue(source, ['autoRunAttemptRun', 'attemptRun'], currentAutoRun.attemptRun),
scheduledAt: readAutoRunStateValue(source, ['scheduledAutoRunAt', 'scheduledAt'], currentAutoRun.scheduledAt),
countdownAt: readAutoRunStateValue(source, ['autoRunCountdownAt', 'countdownAt'], currentAutoRun.countdownAt),
countdownTitle: readAutoRunStateValue(source, ['autoRunCountdownTitle', 'countdownTitle'], currentAutoRun.countdownTitle),
countdownNote: readAutoRunStateValue(source, ['autoRunCountdownNote', 'countdownNote'], currentAutoRun.countdownNote),
@@ -3649,8 +3639,7 @@ function syncAutoRunState(source = {}) {
function isContributionButtonLocked() {
const autoActive = currentAutoRun.autoRunning
|| isAutoRunLockedPhase()
|| isAutoRunPausedPhase()
|| isAutoRunScheduledPhase();
|| isAutoRunPausedPhase();
if (autoActive) {
return false;
}
@@ -3675,12 +3664,8 @@ function isAutoRunWaitingStepPhase() {
return currentAutoRun.phase === 'waiting_step';
}
function isAutoRunScheduledPhase() {
return currentAutoRun.phase === 'scheduled';
}
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 = {}) {
@@ -3717,14 +3702,6 @@ function getAutoRunLabel(payload = currentAutoRun) {
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) {
const rawValue = String(value ?? '').trim();
if (!rawValue) {
@@ -3948,12 +3925,6 @@ function updateFallbackThreadIntervalInputState() {
inputAutoSkipFailuresThreadIntervalMinutes.disabled = Boolean(inputAutoSkipFailures.disabled);
}
function updateAutoDelayInputState() {
const scheduled = isAutoRunScheduledPhase();
inputAutoDelayEnabled.disabled = scheduled;
inputAutoDelayMinutes.disabled = scheduled || !inputAutoDelayEnabled.checked;
}
function formatCountdown(remainingMs) {
const totalSeconds = Math.max(0, Math.ceil(remainingMs / 1000));
const hours = Math.floor(totalSeconds / 3600);
@@ -3974,21 +3945,12 @@ function formatScheduleTime(timestamp) {
});
}
function stopScheduledCountdownTicker() {
clearInterval(scheduledCountdownTimer);
scheduledCountdownTimer = null;
function stopAutoRunCountdownTicker() {
clearInterval(autoRunCountdownTimer);
autoRunCountdownTimer = null;
}
function getActiveAutoRunCountdown() {
if (isAutoRunScheduledPhase() && Number.isFinite(currentAutoRun.scheduledAt)) {
return {
at: currentAutoRun.scheduledAt,
title: '已计划自动运行',
note: `计划于 ${formatScheduleTime(currentAutoRun.scheduledAt)} 开始`,
tone: 'scheduled',
};
}
if (currentAutoRun.phase !== 'waiting_interval') {
return null;
}
@@ -4005,48 +3967,45 @@ function getActiveAutoRunCountdown() {
};
}
function renderScheduledAutoRunInfo() {
if (!autoScheduleBar) {
function renderAutoRunCountdownInfo() {
if (!autoCountdownBar) {
return;
}
const countdown = getActiveAutoRunCountdown();
if (!countdown) {
autoScheduleBar.style.display = 'none';
autoCountdownBar.style.display = 'none';
return;
}
const remainingMs = countdown.at - Date.now();
autoScheduleBar.style.display = 'flex';
autoCountdownBar.style.display = 'flex';
if (btnAutoRunNow) {
btnAutoRunNow.hidden = false;
btnAutoRunNow.textContent = currentAutoRun.phase === 'waiting_interval' ? '立即继续' : '立即开始';
btnAutoRunNow.textContent = '立即继续';
}
if (btnAutoCancelSchedule) {
btnAutoCancelSchedule.hidden = true;
}
autoScheduleTitle.textContent = countdown.title;
autoScheduleMeta.textContent = remainingMs > 0
autoCountdownTitle.textContent = countdown.title;
autoCountdownMeta.textContent = remainingMs > 0
? `${countdown.note ? `${countdown.note}` : ''}剩余 ${formatCountdown(remainingMs)}`
: '倒计时即将结束,正在准备继续...';
return;
}
function syncScheduledCountdownTicker() {
renderScheduledAutoRunInfo();
function syncAutoRunCountdownTicker() {
renderAutoRunCountdownInfo();
if (getActiveAutoRunCountdown()) {
if (scheduledCountdownTimer) {
if (autoRunCountdownTimer) {
return;
}
scheduledCountdownTimer = setInterval(() => {
renderScheduledAutoRunInfo();
autoRunCountdownTimer = setInterval(() => {
renderAutoRunCountdownInfo();
updateStatusDisplay(latestState);
}, 1000);
return;
}
stopScheduledCountdownTicker();
stopAutoRunCountdownTicker();
return;
}
@@ -5159,8 +5118,6 @@ function collectSettingsPayload() {
stepExecutionRangeByFlow: typeof buildStepExecutionRangeByFlowPayload === 'function'
? buildStepExecutionRangeByFlowPayload(latestState?.stepExecutionRangeByFlow)
: (latestState?.stepExecutionRangeByFlow || {}),
autoRunDelayEnabled: inputAutoDelayEnabled.checked,
autoRunDelayMinutes: normalizeAutoDelayMinutes(inputAutoDelayMinutes.value),
autoStepDelaySeconds: normalizeAutoStepDelaySeconds(inputAutoStepDelaySeconds.value),
oauthFlowTimeoutEnabled: typeof inputOAuthFlowTimeoutEnabled !== 'undefined' && inputOAuthFlowTimeoutEnabled
? Boolean(inputOAuthFlowTimeoutEnabled.checked)
@@ -9337,7 +9294,7 @@ function canSelectPhoneSignupMethod() {
}
function isSignupMethodSwitchLocked() {
return isAutoRunLockedPhase() || isAutoRunPausedPhase() || isAutoRunScheduledPhase();
return isAutoRunLockedPhase() || isAutoRunPausedPhase();
}
function updateSignupMethodUI(options = {}) {
@@ -9581,7 +9538,7 @@ function updatePhoneVerificationSettingsUI() {
}
}
phoneSignupReuseUiWasLocked = phoneSignupReuseLocked;
const settingsLocked = isAutoRunLockedPhase() || isAutoRunScheduledPhase();
const settingsLocked = isAutoRunLockedPhase();
if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) {
inputPhoneSignupReloginAfterBindEmail.disabled = settingsLocked || !showPhoneSignupReloginAfterBindEmail;
}
@@ -10744,8 +10701,7 @@ function applyAutoRunStatus(payload = currentAutoRun) {
const runLabel = getAutoRunLabel(currentAutoRun);
const locked = isAutoRunLockedPhase();
const paused = isAutoRunPausedPhase();
const scheduled = isAutoRunScheduledPhase();
const settingsCardLocked = scheduled || locked;
const settingsCardLocked = locked;
setSettingsCardLocked(settingsCardLocked);
setFreePhoneReuseControlsLocked(settingsCardLocked);
@@ -10766,14 +10722,14 @@ function applyAutoRunStatus(payload = currentAutoRun) {
if (typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority) {
inputSub2ApiAccountPriority.disabled = locked;
}
inputAutoSkipFailures.disabled = scheduled;
inputAutoSkipFailures.disabled = locked;
const lockedRunCount = typeof getLockedRunCountFromEmailPool === 'function'
? getLockedRunCountFromEmailPool()
: 0;
const isSyncPhase = typeof isAutoRunSourceSyncPhase === 'function'
? 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'
? shouldSyncRunCountFromAutoRunSource(currentAutoRun)
: (currentAutoRun.autoRunning || isSyncPhase(currentAutoRun.phase));
@@ -10784,10 +10740,6 @@ function applyAutoRunStatus(payload = currentAutoRun) {
}
switch (currentAutoRun.phase) {
case 'scheduled':
autoContinueBar.style.display = 'none';
btnAutoRun.innerHTML = `已计划${runLabel}`;
break;
case 'waiting_step':
autoContinueBar.style.display = 'none';
btnAutoRun.innerHTML = `等待中${runLabel}`;
@@ -10818,10 +10770,9 @@ function applyAutoRunStatus(payload = currentAutoRun) {
break;
}
updateAutoDelayInputState();
updateFallbackThreadIntervalInputState();
syncScheduledCountdownTicker();
updateStopButtonState(scheduled || paused || locked || Object.values(getStepStatuses()).some(status => status === 'running'));
syncAutoRunCountdownTicker();
updateStopButtonState(paused || locked || Object.values(getStepStatuses()).some(status => status === 'running'));
updateConfigMenuControls();
renderContributionMode();
}
@@ -11422,8 +11373,6 @@ function applySettingsState(state) {
if (typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled) {
inputStep6CookieCleanupEnabled.checked = Boolean(state?.step6CookieCleanupEnabled);
}
inputAutoDelayEnabled.checked = Boolean(state?.autoRunDelayEnabled);
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(state?.autoRunDelayMinutes));
inputAutoStepDelaySeconds.value = formatAutoStepDelayInputValue(state?.autoStepDelaySeconds);
if (typeof inputOAuthFlowTimeoutEnabled !== 'undefined' && inputOAuthFlowTimeoutEnabled) {
inputOAuthFlowTimeoutEnabled.checked = state?.oauthFlowTimeoutEnabled !== undefined
@@ -11598,7 +11547,7 @@ function applySettingsState(state) {
}
const isSyncPhase = typeof isAutoRunSourceSyncPhase === 'function'
? 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'
? shouldSyncRunCountFromAutoRunSource(state)
: (Boolean(state?.autoRunning) || isSyncPhase(state?.autoRunPhase ?? state?.phase));
@@ -11608,7 +11557,6 @@ function applySettingsState(state) {
applyAutoRunStatus(state);
markSettingsDirty(false);
updateAutoDelayInputState();
updateFallbackThreadIntervalInputState();
updateAccountRunHistorySettingsUI();
updatePhoneVerificationSettingsUI();
@@ -13657,7 +13605,6 @@ function updateButtonStates() {
const statuses = getNodeStatuses();
const anyRunning = Object.values(statuses).some(s => s === 'running');
const autoLocked = isAutoRunLockedPhase();
const autoScheduled = isAutoRunScheduledPhase();
const enabledNodeIds = getEnabledNodeIdsForStepExecutionRange(latestState);
const icloudTargetMailboxTypeValue = typeof selectIcloudTargetMailboxType !== 'undefined'
? selectIcloudTargetMailboxType?.value
@@ -13671,7 +13618,7 @@ function updateButtonStates() {
if (currentStatus === 'disabled') {
btn.disabled = true;
} else if (anyRunning || autoLocked || autoScheduled) {
} else if (anyRunning || autoLocked) {
btn.disabled = true;
} else if (enabledNodeIds.indexOf(nodeId) === 0) {
btn.disabled = false;
@@ -13691,7 +13638,7 @@ function updateButtonStates() {
const prevNodeId = currentIndex > 0 ? enabledNodeIds[currentIndex - 1] : null;
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.disabled = true;
btn.title = '当前不可跳过';
@@ -13710,8 +13657,8 @@ function updateButtonStates() {
btn.title = `跳过节点 ${nodeId}`;
});
btnReset.disabled = anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked;
const disableIcloudControls = anyRunning || autoScheduled || autoLocked;
btnReset.disabled = anyRunning || isAutoRunPausedPhase() || autoLocked;
const disableIcloudControls = anyRunning || autoLocked;
if (btnIcloudRefresh) btnIcloudRefresh.disabled = disableIcloudControls;
if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = disableIcloudControls || !hasDeletableUsedIcloudAliases();
if (selectIcloudHostPreference) selectIcloudHostPreference.disabled = disableIcloudControls;
@@ -13733,7 +13680,7 @@ function updateButtonStates() {
if (checkboxAutoDeleteIcloud) checkboxAutoDeleteIcloud.disabled = disableIcloudControls;
if (btnContributionMode) btnContributionMode.disabled = isContributionButtonLocked();
applyStepExecutionRangeState(latestState);
updateStopButtonState(anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked);
updateStopButtonState(anyRunning || isAutoRunPausedPhase() || autoLocked);
renderContributionMode();
}
@@ -13753,18 +13700,7 @@ function updateStatusDisplay(state) {
displayStatus.textContent = remainingMs > 0
? `${countdown.title},剩余 ${formatCountdown(remainingMs)}`
: `${countdown.title},即将结束...`;
statusBar.classList.add(countdown.tone === 'scheduled' ? 'scheduled' : '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');
statusBar.classList.add('running');
return;
}
@@ -14973,7 +14909,7 @@ btnSaveSettings.addEventListener('click', async () => {
btnStop.addEventListener('click', async () => {
btnStop.disabled = true;
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) => {
@@ -15150,24 +15086,18 @@ async function startAutoRunFromCurrentSettings() {
btnAutoRun.disabled = true;
inputRunCount.disabled = true;
const delayEnabled = inputAutoDelayEnabled.checked;
const delayMinutes = normalizeAutoDelayMinutes(inputAutoDelayMinutes.value);
const activeFlowId = typeof getSelectedFlowId === 'function'
? getSelectedFlowId(latestState)
: (String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID);
const targetId = typeof getSelectedTargetId === 'function'
? getSelectedTargetId(activeFlowId)
: normalizeTargetIdForFlow(activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlow(activeFlowId));
inputAutoDelayMinutes.value = String(delayMinutes);
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> 运行中...';
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> 运行中...';
const response = await sendSidepanelMessage({
type: delayEnabled ? 'SCHEDULE_AUTO_RUN' : 'AUTO_RUN',
type: 'AUTO_RUN',
source: 'sidepanel',
payload: {
totalRuns,
delayMinutes,
activeFlowId,
targetId,
autoRunSkipFailures,
@@ -15213,15 +15143,12 @@ btnAutoContinue.addEventListener('click', async () => {
btnAutoRunNow?.addEventListener('click', async () => {
try {
btnAutoRunNow.disabled = true;
const waitingInterval = currentAutoRun.phase === 'waiting_interval';
await sendSidepanelMessage({
type: waitingInterval ? 'SKIP_AUTO_RUN_COUNTDOWN' : 'START_SCHEDULED_AUTO_RUN_NOW',
type: 'SKIP_AUTO_RUN_COUNTDOWN',
source: 'sidepanel',
payload: {},
});
if (waitingInterval) {
showToast('已跳过当前倒计时,自动流程将立即继续。', 'info', 1800);
}
showToast('已跳过当前倒计时,自动流程将立即继续。', 'info', 1800);
} catch (err) {
showToast(err.message, 'error');
} 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
btnReset.addEventListener('click', async () => {
const confirmed = await openConfirmModal({
@@ -15270,7 +15185,6 @@ btnReset.addEventListener('click', async () => {
autoRunCurrentRun: 0,
autoRunTotalRuns: 1,
autoRunAttemptRun: 0,
scheduledAutoRunAt: null,
autoRunCountdownAt: null,
autoRunCountdownTitle: '',
autoRunCountdownNote: '',
@@ -15460,6 +15374,10 @@ btnOpenKiroRsGithub?.addEventListener('click', () => {
openExternalUrl('https://github.com/QLHazyCoder/kiro.rs');
});
btnOpenWebchat2ApiGithub?.addEventListener('click', () => {
openExternalUrl('https://github.com/zqbxdev/webchat2api');
});
btnGpcHelperBalance?.addEventListener('click', async () => {
try {
const response = await chrome.runtime.sendMessage({
@@ -16576,12 +16494,6 @@ inputAutoSkipFailuresThreadIntervalMinutes.addEventListener('blur', () => {
saveSettings({ silent: true }).catch(() => { });
});
inputAutoDelayEnabled.addEventListener('change', () => {
updateAutoDelayInputState();
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
});
inputStep6CookieCleanupEnabled?.addEventListener('change', () => {
markSettingsDirty(true);
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 = {}) {
const normalizedProvider = normalizePhoneSmsProvider(provider);
const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback;
@@ -17457,7 +17358,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
password: null,
nodeStatuses: NODE_DEFAULT_STATUSES,
logs: [],
scheduledAutoRunAt: null,
autoRunCountdownAt: null,
autoRunCountdownTitle: '',
autoRunCountdownNote: '',
@@ -17492,7 +17392,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
autoRunCurrentRun: 0,
autoRunTotalRuns: 1,
autoRunAttemptRun: 0,
scheduledAutoRunAt: null,
autoRunCountdownAt: null,
autoRunCountdownTitle: '',
autoRunCountdownNote: '',
@@ -17926,10 +17825,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
inputAutoSkipFailures.checked = Boolean(message.payload.autoRunSkipFailures);
updateFallbackThreadIntervalInputState();
}
if (message.payload.autoRunDelayEnabled !== undefined) {
inputAutoDelayEnabled.checked = Boolean(message.payload.autoRunDelayEnabled);
updateAutoDelayInputState();
}
if (
message.payload.step6CookieCleanupEnabled !== undefined
&& typeof inputStep6CookieCleanupEnabled !== 'undefined'
@@ -17945,9 +17840,6 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
renderStepStatuses(latestState);
updateButtonStates();
}
if (message.payload.autoRunDelayMinutes !== undefined) {
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(message.payload.autoRunDelayMinutes));
}
if (message.payload.autoRunFallbackThreadIntervalMinutes !== undefined) {
inputAutoSkipFailuresThreadIntervalMinutes.value = String(
normalizeAutoRunThreadIntervalMinutes(message.payload.autoRunFallbackThreadIntervalMinutes)
@@ -18239,12 +18131,11 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
case 'AUTO_RUN_STATUS': {
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,
autoRunCurrentRun: message.payload.currentRun,
autoRunTotalRuns: message.payload.totalRuns,
autoRunAttemptRun: message.payload.attemptRun,
scheduledAutoRunAt: message.payload.scheduledAt ?? null,
autoRunCountdownAt: message.payload.countdownAt ?? null,
autoRunCountdownTitle: message.payload.countdownTitle ?? '',
autoRunCountdownNote: message.payload.countdownNote ?? '',
@@ -18252,7 +18143,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
applyAutoRunStatus(message.payload);
updateStatusDisplay(latestState);
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();
}
break;
+16 -32
View File
@@ -134,8 +134,6 @@ test('auto-run controller skips add-phone failures to the next round instead of
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: '163',
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 });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -297,8 +295,6 @@ test('auto-run controller treats phone-number supply exhaustion as round-fatal a
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: '163',
emailGenerator: 'duck',
@@ -348,7 +344,7 @@ test('auto-run controller treats phone-number supply exhaustion as round-fatal a
events.broadcasts.push({ phase, ...payload });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -460,8 +456,6 @@ test('auto-run controller treats ended GPC task as round-fatal and skips same-ro
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: '163',
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 });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -621,8 +615,6 @@ test('auto-run controller keeps same-round retrying for step9 local replacement
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: '163',
emailGenerator: 'duck',
@@ -672,7 +664,7 @@ test('auto-run controller keeps same-round retrying for step9 local replacement
events.broadcasts.push({ phase, ...payload });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
autoRunTotalRuns: payload.totalRuns ?? runtime.state.autoRunTotalRuns,
@@ -688,7 +680,7 @@ test('auto-run controller keeps same-round retrying for step9 local replacement
return sessionSeed;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -779,8 +771,6 @@ test('auto-run controller skips user_already_exists failures to the next round i
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: '163',
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 });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -943,8 +933,6 @@ test('auto-run controller skips step 4 repeated 405 recovery failures to the nex
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: '163',
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 });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -1108,8 +1096,6 @@ test('auto-run controller keeps retrying the same custom mail provider pool emai
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: 'custom',
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 });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -1274,8 +1260,6 @@ test('auto-run controller retries 5sim rate limit failures instead of treating c
customPassword: '',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: '163',
emailGenerator: 'duck',
@@ -1325,7 +1309,7 @@ test('auto-run controller retries 5sim rate limit failures instead of treating c
events.broadcasts.push({ phase, ...payload });
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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;
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -130,8 +130,6 @@ let currentState = {
customPassword: '',
autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
signupMethod: 'phone',
resolvedSignupMethod: 'phone',
@@ -204,8 +202,6 @@ async function resetState() {
customPassword: prev.customPassword,
autoRunSkipFailures: prev.autoRunSkipFailures,
autoRunFallbackThreadIntervalMinutes: prev.autoRunFallbackThreadIntervalMinutes,
autoRunDelayEnabled: prev.autoRunDelayEnabled,
autoRunDelayMinutes: prev.autoRunDelayMinutes,
autoStepDelaySeconds: prev.autoStepDelaySeconds,
signupMethod: prev.signupMethod,
resolvedSignupMethod: null,
+2 -4
View File
@@ -104,8 +104,6 @@ test('auto-run controller verifies hotmail mailbox before each fresh attempt sta
customPassword: '',
autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
mailProvider: 'hotmail-api',
emailGenerator: 'duck',
@@ -149,7 +147,7 @@ test('auto-run controller verifies hotmail mailbox before each fresh attempt sta
broadcastAutoRunStatus: async (phase, payload = {}) => {
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,
autoRunCurrentRun: payload.currentRun ?? runtime.state.autoRunCurrentRun,
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 });
},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
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,
autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
signupMethod: 'email',
stepExecutionRangeByFlow: {
@@ -94,7 +92,7 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
currentState = {
...currentState,
...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,
autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0,
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 () => {},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
@@ -179,8 +177,6 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
phoneSignupReloginAfterBindEmailEnabled: false,
autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
signupMethod: 'email',
stepExecutionRangeByFlow: {
@@ -445,8 +441,6 @@ test('auto-run controller stops immediately on kiro proxy failures even when ski
flowId: 'kiro',
autoRunSkipFailures: true,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
nodeStatuses: {
'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 });
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,
autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0,
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 () => {},
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,
autoRunCurrentRun: payload.currentRun ?? 0,
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 () => {
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;
@@ -76,14 +75,15 @@ let autoRunAttemptRun = 0;
let autoRunSessionId = 0;
const state = {
autoRunDelayEnabled: false,
autoRunTimerPlan: {
kind: AUTO_RUN_TIMER_KIND_SCHEDULED_START,
kind: AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
fireAt: Date.now() + 60_000,
totalRuns: 2,
autoRunSkipFailures: false,
autoRunSessionId: 42,
countdownTitle: '已计划自动运行',
currentRun: 1,
attemptRun: 1,
countdownTitle: '等待继续',
countdownNote: '等待启动',
},
};
@@ -106,7 +106,6 @@ async function clearAutoRunTimerAlarm() {
async function broadcastAutoRunStatus() {}
async function addLog() {}
async function setAutoRunDelayEnabledState() {}
function serializeAutoRunRoundSummaries(totalRuns, summaries = []) {
return Array.isArray(summaries) ? summaries : [];
}
@@ -137,116 +136,10 @@ return {
const started = await api.launchAutoRunTimerPlan('alarm');
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(snapshot.startCalls, 0);
assert.equal(snapshot.clearStopCalls, 0);
assert.equal(snapshot.clearAlarmCalls, 1);
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.clearAlarmCalls, 0);
assert.equal(snapshot.autoRunCurrentRun, 0);
assert.equal(snapshot.autoRunTotalRuns, 1);
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 normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
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 normalizeMailProvider(value) { return String(value || '').trim().toLowerCase() || '163'; }
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 normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
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 normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; }
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 normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
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 normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; }
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 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: {},
}),
normalizeRunCount: (value) => Number(value) || 1,
scheduleAutoRun: async () => {
calls.push({ type: 'scheduleAutoRun' });
return { ok: true };
},
setState: async (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 不支持手机号注册。/
);
await assert.rejects(
() => router.handleMessage({
type: 'SCHEDULE_AUTO_RUN',
payload: {
totalRuns: 2,
delayMinutes: 5,
autoRunSkipFailures: false,
},
}),
/当前 flow 不支持手机号注册。/
);
assert.deepStrictEqual(calls, []);
});
-3
View File
@@ -99,9 +99,6 @@ function normalizeMailProvider(value = '') {
function normalizeAutoRunFallbackThreadIntervalMinutes(value) {
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) {
const numeric = Number(value);
return Number.isFinite(numeric) ? Math.max(0, Math.floor(numeric)) : fallback;
@@ -35,7 +35,6 @@ function createRouterWithFinalNode(options = {}) {
buildLuckmailSessionSettingsPayload: () => ({}),
buildPersistentSettingsPayload: () => ({}),
broadcastDataUpdate: () => {},
cancelScheduledAutoRun: async () => {},
checkIcloudSession: async () => {},
clearAutoRunTimerAlarm: async () => {},
clearLuckmailRuntimeState: async () => {},
@@ -87,7 +86,6 @@ function createRouterWithFinalNode(options = {}) {
listLuckmailPurchasesForManagement: async () => [],
normalizeHotmailAccounts: (items) => items,
normalizeRunCount: (value) => value,
AUTO_RUN_TIMER_KIND_SCHEDULED_START: 'scheduled',
notifyNodeComplete: () => {},
notifyNodeError: () => {},
patchHotmailAccount: async () => {},
@@ -96,7 +94,6 @@ function createRouterWithFinalNode(options = {}) {
requestStop: async () => {},
resetState: async () => {},
resumeAutoRun: async () => {},
scheduleAutoRun: async () => {},
selectLuckmailPurchase: async () => {},
setCurrentHotmailAccount: async () => {},
setCurrentMail2925Account: async () => {},
@@ -103,7 +103,6 @@ function createRouter(overrides = {}) {
broadcastDataUpdate: (updates) => {
events.broadcasts.push(updates);
},
cancelScheduledAutoRun: async () => {},
checkIcloudSession: async () => {},
clearAutoRunTimerAlarm: async () => {},
clearLuckmailRuntimeState: async () => {},
@@ -171,7 +170,6 @@ function createRouter(overrides = {}) {
listLuckmailPurchasesForManagement: async () => [],
normalizeHotmailAccounts: (items) => items,
normalizeRunCount: (value) => value,
AUTO_RUN_TIMER_KIND_SCHEDULED_START: 'scheduled',
notifyNodeComplete: (nodeId, payload) => {
events.notifyCompletions.push({ step: getStepForNode(nodeId), nodeId, payload });
},
@@ -183,7 +181,6 @@ function createRouter(overrides = {}) {
requestStop: async () => {},
resetState: async () => {},
resumeAutoRun: async () => {},
scheduleAutoRun: async () => {},
selectLuckmailPurchase: async () => {},
setCurrentHotmailAccount: async () => {},
setEmailState: async (email) => {
-2
View File
@@ -195,7 +195,6 @@ let step8TabUpdatedListener = null;
let step8PendingReject = null;
let resumeWaiter = null;
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
const DEFAULT_STATE = {
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() {
return null;
}
async function cancelScheduledAutoRun() {}
async function clearAutoRunTimerAlarm() {}
function clearStopRequest() {
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.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.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('unknown'), '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('kiro', 'anything-else'), 'kiro-rs');
assert.equal(flowRegistry.normalizeTargetId('grok', 'anything-else'), 'webchat2api');
assert.deepEqual(
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(
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(
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(
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,
['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.getFlowCapabilities('openai').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 () => {},
sendToContentScriptResilient: async (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' };
},
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.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.message.type, 'EXECUTE_NODE');
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');
});
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 () => {
const api = loadGrokRunnerApi();
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, 'isAutoRunLockedPhase'),
extractFunction(source, 'isAutoRunPausedPhase'),
extractFunction(source, 'isAutoRunScheduledPhase'),
extractFunction(source, 'applyAutoRunStatus'),
].join('\n');
@@ -96,7 +95,6 @@ let currentAutoRun = {
currentRun: 0,
totalRuns: 1,
attemptRun: 0,
scheduledAt: null,
countdownAt: null,
countdownTitle: '',
countdownNote: '',
@@ -116,9 +114,8 @@ function getLockedRunCountFromEmailPool() { return lockedRunCount; }
function isCustomMailProvider() { return false; }
function usesCustomEmailPoolGenerator() { return false; }
function setDefaultAutoRunButton() {}
function updateAutoDelayInputState() {}
function updateFallbackThreadIntervalInputState() {}
function syncScheduledCountdownTicker() {}
function syncAutoRunCountdownTicker() {}
function updateStopButtonState() {}
function getStepStatuses() { return {}; }
function updateConfigMenuControls() {}
@@ -171,7 +168,6 @@ test('sidepanel pending auto-run start ignores stale active run count sync', ()
extractFunction(source, 'syncAutoRunState'),
extractFunction(source, 'isAutoRunLockedPhase'),
extractFunction(source, 'isAutoRunPausedPhase'),
extractFunction(source, 'isAutoRunScheduledPhase'),
extractFunction(source, 'isAutoRunSourceSyncPhase'),
extractFunction(source, 'shouldSyncRunCountFromAutoRunSource'),
extractFunction(source, 'applyAutoRunStatus'),
@@ -184,7 +180,6 @@ let currentAutoRun = {
currentRun: 0,
totalRuns: 1,
attemptRun: 0,
scheduledAt: null,
countdownAt: null,
countdownTitle: '',
countdownNote: '',
@@ -205,9 +200,8 @@ function getLockedRunCountFromEmailPool() { return 0; }
function isCustomMailProvider() { return false; }
function usesCustomEmailPoolGenerator() { return false; }
function setDefaultAutoRunButton() {}
function updateAutoDelayInputState() {}
function updateFallbackThreadIntervalInputState() {}
function syncScheduledCountdownTicker() {}
function syncAutoRunCountdownTicker() {}
function updateStopButtonState() {}
function getStepStatuses() { return {}; }
function updateConfigMenuControls() {}
@@ -73,8 +73,6 @@ const inputAutoSkipFailures = { checked: false };
const inputContributionNickname = { value: 'tester' };
const inputContributionQq = { value: '123456' };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const btnAutoRun = { disabled: false, innerHTML: '' };
const inputRunCount = { disabled: false };
const selectFlow = { value: latestState.activeFlowId };
@@ -132,7 +130,6 @@ function shouldWarnAutoRunFallbackRisk() { return false; }
function isAutoRunFallbackRiskPromptDismissed() { return false; }
async function openAutoRunFallbackRiskConfirmModal() { throw new Error('should not be called'); }
function setAutoRunFallbackRiskPromptDismissed() {}
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
async function refreshContributionContentHint() {
events.push({ type: 'refresh' });
${refreshImpl ? 'return (' + refreshImpl + ')();' : 'return null;'}
@@ -272,8 +269,6 @@ const inputAutoSkipFailures = { checked: false };
const inputContributionNickname = { value: 'tester' };
const inputContributionQq = { value: '123456' };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const btnAutoRun = { disabled: false, innerHTML: '' };
const inputRunCount = { disabled: false, value: '1' };
const inputPhoneVerificationEnabled = { checked: true };
@@ -324,7 +319,6 @@ function shouldWarnAutoRunFallbackRisk() { return false; }
function isAutoRunFallbackRiskPromptDismissed() { return false; }
async function openAutoRunFallbackRiskConfirmModal() { throw new Error('should not be called'); }
function setAutoRunFallbackRiskPromptDismissed() {}
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
async function refreshContributionContentHint() {
events.push({ type: 'refresh' });
return null;
@@ -247,8 +247,6 @@ const inputTempEmailReceiveMailbox = { value: 'relay@example.com' };
const inputTempEmailUseRandomSubdomain = { checked: true };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
const inputAutoDelayEnabled = { checked: true };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '10' };
const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '6' };
@@ -297,7 +295,6 @@ function normalizeLuckmailEmailType(value) { return String(value || '').trim();
function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
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; }
@@ -38,7 +38,12 @@ test('sidepanel html exposes flow selector and kiro source fields', () => {
'id="select-flow"',
'<option value="grok">Grok</option>',
'id="label-source-selector"',
'id="btn-open-webchat2api-github"',
'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="btn-open-kiro-rs-github"',
'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/);
});
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', () => {
const bundle = [
extractFunction(sidepanelSource, 'normalizeSignupMethod'),
-7
View File
@@ -132,8 +132,6 @@ const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '' };
const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '4' };
@@ -220,7 +218,6 @@ function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value |
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
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; }
@@ -408,8 +405,6 @@ const selectLuckmailEmailType = { value: 'ms_graph' };
const inputLuckmailDomain = { value: '' };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '' };
const inputAutoStepDelaySeconds = { value: '' };
const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '' };
@@ -483,7 +478,6 @@ function applyCloudflareTempEmailSettingsState() {}
function renderCloudflareDomainOptions() {}
function setCloudflareDomainEditMode() {}
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function formatAutoStepDelayInputValue(value) { return value == null ? '' : String(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizeHeroSmsMaxPriceValue(value = '') { return String(value || '').trim(); }
@@ -519,7 +513,6 @@ function updateHeroSmsPlatformDisplay() {}
function updatePhoneSmsProviderOrderSummary() {}
function applyAutoRunStatus() {}
function markSettingsDirty() {}
function updateAutoDelayInputState() {}
function updateFallbackThreadIntervalInputState() {}
function updateAccountRunHistorySettingsUI() {}
function updatePhoneVerificationSettingsUI() {}
@@ -203,8 +203,6 @@ const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '' };
const inputOAuthFlowTimeoutEnabled = { checked: true };
const inputVerificationResendCount = { value: '4' };
@@ -258,7 +256,6 @@ function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value |
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
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; }
+17 -5
View File
@@ -38,25 +38,37 @@ function extractFunction(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="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 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 stepRangeIndex = html.indexOf('id="row-step-execution-range"');
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(autoDelayIndex, -1);
assert.notEqual(sharedAutoRunIndex, -1);
assert.notEqual(threadIntervalIndex, -1);
assert.notEqual(oauthTimeoutIndex, -1);
assert.notEqual(stepRangeIndex, -1);
assert.notEqual(oauthDisplayIndex, -1);
assert.ok(autoDelayIndex > step6CookieIndex, 'startup delay row should render below the openai step6 cookie row');
assert.ok(stepRangeIndex > autoDelayIndex, 'step execution range should still remain below the startup delay row');
assert.notEqual(oauthCallbackIndex, -1);
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 < 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', () => {
@@ -713,7 +713,6 @@ function setFreePhoneReuseControlsLocked(locked) {
|| !Boolean(inputPhoneVerificationEnabled.checked && phoneVerificationSectionExpanded);
}
function isAutoRunLockedPhase() { return false; }
function isAutoRunScheduledPhase() { return false; }
${extractFunction('normalizeSignupMethod')}
${extractFunction('normalizeHeroSmsReuseEnabledValue')}
@@ -979,8 +978,6 @@ const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoDelayEnabled = { checked: false };
const inputAutoDelayMinutes = { value: '30' };
const inputAutoStepDelaySeconds = { value: '' };
const inputPhoneVerificationEnabled = { checked: true };
const inputFreePhoneReuseEnabled = { checked: true };
@@ -1072,7 +1069,6 @@ function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value |
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
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; }
-4
View File
@@ -76,7 +76,6 @@ let autoRunCurrentRun = 2;
let autoRunTotalRuns = 3;
let autoRunAttemptRun = 4;
let autoRunSessionId = 99;
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
const DEFAULT_STATE = {
nodeStatuses: {},
};
@@ -158,9 +157,6 @@ async function getState() {
function getPendingAutoRunTimerPlan() {
return null;
}
function isAutoRunScheduledState() {
return false;
}
function getStep8CallbackUrlFromNavigation() { return ''; }
function getStep8CallbackUrlFromTabUpdate() { return ''; }
function getStep8EffectLabel() { return 'no_effect'; }