refactor shared flow settings and runtime cleanup
This commit is contained in:
+5
-231
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 || ''),
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 || ''),
|
||||
|
||||
@@ -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'] || {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 }, {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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": {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'; }
|
||||
|
||||
@@ -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, []);
|
||||
});
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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']
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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'; }
|
||||
|
||||
Reference in New Issue
Block a user