Refactor workflow auto-run to node graph

This commit is contained in:
QLHazyCoder
2026-05-15 17:41:35 +08:00
parent f6f804f1a2
commit 81fc40706a
76 changed files with 4028 additions and 1453 deletions
+2 -2
View File
@@ -7,7 +7,7 @@
chrome,
cleanupStep8NavigationListeners,
clickWithDebugger,
completeStepFromBackground,
completeNodeFromBackground,
ensureStep8SignupPageReady,
getOAuthFlowRemainingMs,
getOAuthFlowStepTimeoutMs,
@@ -122,7 +122,7 @@
cleanupListener();
addStepLog(visibleStep, `已捕获 localhost 地址:${callbackUrl}`, 'ok').then(() => {
return completeStepFromBackground(visibleStep, { localhostUrl: callbackUrl });
return completeNodeFromBackground(state?.nodeId || 'confirm-oauth', { localhostUrl: callbackUrl });
}).then(() => {
resolve();
}).catch((err) => {
+3 -3
View File
@@ -15,7 +15,7 @@
const {
addLog: rawAddLog = async () => {},
chrome,
completeStepFromBackground,
completeNodeFromBackground,
createAutomationTab = null,
ensureContentScriptReadyOnTabUntilStopped,
fetch: fetchImpl = null,
@@ -457,7 +457,7 @@
gopayHelperOrderCreatedAt: result.orderCreatedAt || Date.now(),
});
await addLog(`步骤 6GPC ${result.phoneMode === GPC_HELPER_PHONE_MODE_AUTO ? '自动' : '手动'}模式任务已创建(task_id: ${result.taskId}),准备继续下一步。`, 'info');
await completeStepFromBackground(6, {
await completeNodeFromBackground('plus-checkout-create', {
plusCheckoutCountry: result.country || 'ID',
plusCheckoutCurrency: result.currency || 'IDR',
plusCheckoutSource: result.checkoutSource,
@@ -517,7 +517,7 @@
await addLog(`步骤 6Plus Checkout 页面已就绪(${paymentMethodLabel} / ${result.country || 'DE'} ${result.currency || 'EUR'}),准备继续下一步。`, 'info');
await completeStepFromBackground(6, {
await completeNodeFromBackground('plus-checkout-create', {
plusCheckoutCountry: result.country || 'DE',
plusCheckoutCurrency: result.currency || 'EUR',
});
+10 -10
View File
@@ -11,7 +11,7 @@
chrome,
CLOUDFLARE_TEMP_EMAIL_PROVIDER,
CLOUD_MAIL_PROVIDER = 'cloudmail',
completeStepFromBackground,
completeNodeFromBackground,
confirmCustomVerificationStepBypass,
ensureMail2925MailboxSession,
ensureIcloudMailSession,
@@ -225,8 +225,8 @@
`步骤 ${visibleStep}:当前认证页已进入 OAuth 授权页${fromRecovery ? '(轮询失败后复核)' : ''},跳过登录验证码拉取并继续后续流程。`,
'warn'
);
if (typeof completeStepFromBackground === 'function') {
await completeStepFromBackground(visibleStep, {
if (typeof completeNodeFromBackground === 'function') {
await completeNodeFromBackground(options.nodeId || 'fetch-login-code', {
loginVerificationRequestedAt: null,
skipLoginVerificationStep: true,
directOAuthConsentPage: true,
@@ -316,7 +316,7 @@
),
});
if (pageState?.state === 'oauth_consent_page') {
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, { fromRecovery: true });
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, { fromRecovery: true, nodeId: currentState?.nodeId });
return { outcome: 'completed' };
}
if (pageState?.state === 'verification_page' || pageState?.state === 'phone_verification_page' || pageState?.state === 'add_email_page') {
@@ -401,7 +401,7 @@
visibleStep,
});
await completeStepFromBackground(visibleStep, {
await completeNodeFromBackground(state?.nodeId || 'fetch-login-code', {
phoneVerification: true,
loginPhoneVerification: true,
code: result?.code || '',
@@ -438,7 +438,7 @@
timeoutMs: await getStep8ReadyTimeoutMs('确认登录验证码页已就绪', state?.oauthUrl || '', visibleStep),
});
if (pageState?.state === 'oauth_consent_page') {
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep);
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, { nodeId: state?.nodeId });
return;
}
if (pageState?.state === 'phone_verification_page') {
@@ -450,7 +450,7 @@
preparedState = addEmailPreparation?.state || preparedState;
pageState = addEmailPreparation?.pageState || pageState;
if (pageState?.state === 'oauth_consent_page') {
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep);
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, { nodeId: preparedState?.nodeId || state?.nodeId });
return;
}
if (pageState?.state === 'phone_verification_page') {
@@ -576,7 +576,7 @@
let stickyLastResendAt = Number(state?.loginVerificationRequestedAt) || 0;
let retryWithoutStep7Streak = 0;
const maxRetryWithoutStep7Streak = 3;
let currentStepRecoveryAttempt = 0;
let currentNodeRecoveryAttempt = 0;
while (true) {
try {
@@ -601,8 +601,8 @@
let retryWithoutStep7 = false;
if (isStep8EmailInUseError(currentError) || isStep8MaxCheckAttemptsError(currentError)) {
currentStepRecoveryAttempt += 1;
if (currentStepRecoveryAttempt > STEP8_CURRENT_STEP_RECOVERY_MAX_ATTEMPTS) {
currentNodeRecoveryAttempt += 1;
if (currentNodeRecoveryAttempt > STEP8_CURRENT_STEP_RECOVERY_MAX_ATTEMPTS) {
throw currentError;
}
if (isStep8EmailInUseError(currentError)) {
+3 -3
View File
@@ -7,7 +7,7 @@
const {
addLog,
chrome,
completeStepFromBackground,
completeNodeFromBackground,
confirmCustomVerificationStepBypass,
generateRandomBirthday,
generateRandomName,
@@ -83,7 +83,7 @@
return result || {};
}
await completeStepFromBackground(4, {
await completeNodeFromBackground('fetch-signup-code', {
phoneVerification: true,
code: result?.code || '',
...(result?.skipProfileStep ? { skipProfileStep: true } : {}),
@@ -280,7 +280,7 @@
throw new Error(prepareResult.error);
}
if (prepareResult?.alreadyVerified) {
await completeStepFromBackground(4, prepareResult?.skipProfileStep ? { skipProfileStep: true } : {});
await completeNodeFromBackground('fetch-signup-code', prepareResult?.skipProfileStep ? { skipProfileStep: true } : {});
return;
}
+2 -1
View File
@@ -105,7 +105,8 @@
`步骤 3:正在填写密码,${identityLabel},密码为${state.customPassword ? '自定义' : '自动生成'}${password.length} 位)`
);
await sendToContentScript('signup-page', {
type: 'EXECUTE_STEP',
type: 'EXECUTE_NODE',
nodeId: 'fill-password',
step: 3,
source: 'background',
payload: {
+3 -3
View File
@@ -79,7 +79,7 @@
addLog: rawAddLog = async () => {},
broadcastDataUpdate,
chrome,
completeStepFromBackground,
completeNodeFromBackground,
ensureContentScriptReadyOnTabUntilStopped,
fetch: fetchImpl = null,
generateRandomName,
@@ -1018,7 +1018,7 @@
plusCheckoutSource: PLUS_PAYMENT_METHOD_GPC_HELPER,
});
await addLog('步骤 7:GPC 任务已完成,准备继续下一步。', 'ok');
await completeStepFromBackground(7, {
await completeNodeFromBackground('plus-checkout-billing', {
plusCheckoutSource: PLUS_PAYMENT_METHOD_GPC_HELPER,
});
return;
@@ -1920,7 +1920,7 @@
throw new Error(`步骤 7:多次提交账单地址后仍未跳转到 ${paymentConfig.label}${lastSubmitError}`);
}
await completeStepFromBackground(7, {
await completeNodeFromBackground('plus-checkout-billing', {
plusBillingCountryText: result?.countryText || '',
});
}
+9 -2
View File
@@ -16,10 +16,17 @@
await addLog(`步骤 5:已生成姓名 ${firstName} ${lastName},生日 ${year}-${month}-${day}`);
await sendToContentScript('signup-page', {
type: 'EXECUTE_STEP',
type: 'EXECUTE_NODE',
nodeId: 'fill-profile',
step: 5,
source: 'background',
payload: { firstName, lastName, year, month, day },
payload: {
firstName,
lastName,
year,
month,
day,
},
});
}
+2 -2
View File
@@ -17,7 +17,7 @@
const {
addLog,
chrome,
completeStepFromBackground,
completeNodeFromBackground,
ensureContentScriptReadyOnTabUntilStopped,
getTabId,
isTabAlive,
@@ -1433,7 +1433,7 @@
}
await setState({ plusGoPayApprovedAt: Date.now() });
await completeStepFromBackground(8, {
await completeNodeFromBackground('paypal-approve', {
plusGoPayApprovedAt: Date.now(),
});
}
+5 -4
View File
@@ -4,7 +4,7 @@
function createStep7Executor(deps = {}) {
const {
addLog,
completeStepFromBackground,
completeNodeFromBackground,
getErrorMessage,
getLoginAuthStateLabel,
getOAuthFlowStepTimeoutMs,
@@ -131,7 +131,7 @@
step: completionStep,
visibleStep: completionStep,
});
await completeStepFromBackground(completionStep, {
await completeNodeFromBackground(state?.nodeId || 'oauth-login', {
loginVerificationRequestedAt: null,
skipLoginVerificationStep: true,
directOAuthConsentPage: true,
@@ -225,7 +225,8 @@
const result = await sendToContentScriptResilient(
'signup-page',
{
type: 'EXECUTE_STEP',
type: 'EXECUTE_NODE',
nodeId: 'oauth-login',
step: 7,
source: 'background',
payload: {
@@ -277,7 +278,7 @@
completionPayload.directOAuthConsentPage = Boolean(result.directOAuthConsentPage);
}
await completeStepFromBackground(completionStep, completionPayload);
await completeNodeFromBackground(state?.nodeId || 'oauth-login', completionPayload);
return;
}
+2 -2
View File
@@ -102,7 +102,7 @@
const {
addLog,
chrome: chromeApi = globalThis.chrome,
completeStepFromBackground,
completeNodeFromBackground,
openSignupEntryTab,
} = deps;
@@ -139,7 +139,7 @@
await clearOpenAiCookiesBeforeStep1();
await addLog('步骤 1:正在打开 ChatGPT 官网...');
await openSignupEntryTab(1);
await completeStepFromBackground(1, {});
await completeNodeFromBackground('open-chatgpt', {});
}
return { executeStep1 };
+2 -2
View File
@@ -11,7 +11,7 @@
const {
addLog,
chrome,
completeStepFromBackground,
completeNodeFromBackground,
ensureContentScriptReadyOnTabUntilStopped,
getTabId,
isTabAlive,
@@ -294,7 +294,7 @@
await sleepWithStop(500);
}
await completeStepFromBackground(8, {
await completeNodeFromBackground('paypal-approve', {
plusPaypalApprovedAt: Date.now(),
});
}
+5 -5
View File
@@ -6,7 +6,7 @@
addLog,
chrome,
closeConflictingTabsForSource,
completeStepFromBackground,
completeNodeFromBackground,
ensureContentScriptReadyOnTab,
getPanelMode,
getTabId,
@@ -253,7 +253,7 @@
if (shouldBypassStep9ForLocalCpa(state)) {
await addStepLog(platformVerifyStep, '检测到本地 CPA,且当前策略为“跳过平台回调验证”,本轮不再重复提交回调地址。', 'info');
await completeStepFromBackground(platformVerifyStep, {
await completeNodeFromBackground(state?.nodeId || 'platform-verify', {
localhostUrl: state.localhostUrl,
verifiedStatus: 'local-auto',
});
@@ -286,7 +286,7 @@
|| normalizeString(result?.status_message)
|| 'CPA 已通过接口提交回调';
await addStepLog(platformVerifyStep, verifiedStatus, 'ok');
await completeStepFromBackground(platformVerifyStep, {
await completeNodeFromBackground(state?.nodeId || 'platform-verify', {
localhostUrl: callback.url,
verifiedStatus,
});
@@ -336,7 +336,7 @@
const verifiedStatus = normalizeString(result?.message) || 'Codex2API OAuth 账号添加成功';
await addStepLog(platformVerifyStep, verifiedStatus, 'ok');
await completeStepFromBackground(platformVerifyStep, {
await completeNodeFromBackground(state?.nodeId || 'platform-verify', {
localhostUrl: callback.url,
verifiedStatus,
});
@@ -381,7 +381,7 @@
logOptions: { step: visibleStep, stepKey: 'platform-verify' },
timeoutMs: SUB2API_STEP9_RESPONSE_TIMEOUT_MS,
});
await completeStepFromBackground(platformVerifyStep, result);
await completeNodeFromBackground(state?.nodeId || 'platform-verify', result);
return;
} catch (error) {
lastError = error;
+2 -2
View File
@@ -9,7 +9,7 @@
function createPlusReturnConfirmExecutor(deps = {}) {
const {
addLog,
completeStepFromBackground,
completeNodeFromBackground,
getTabId,
isTabAlive,
setState,
@@ -53,7 +53,7 @@
plusCheckoutTabId: tabId,
plusReturnUrl: tab?.url || '',
});
await completeStepFromBackground(9, {
await completeNodeFromBackground('plus-checkout-return', {
plusReturnUrl: tab?.url || '',
});
}
+20 -19
View File
@@ -1,48 +1,49 @@
(function attachBackgroundStepRegistry(root, factory) {
root.MultiPageBackgroundStepRegistry = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStepRegistryModule() {
function createStepRegistry(definitions = []) {
function createNodeRegistry(definitions = []) {
const ordered = (Array.isArray(definitions) ? definitions : [])
.map((definition) => ({
id: Number(definition?.id),
order: Number(definition?.order),
key: String(definition?.key || '').trim(),
nodeId: String(definition?.nodeId || definition?.key || '').trim(),
displayOrder: Number(definition?.displayOrder ?? definition?.order),
executeKey: String(definition?.executeKey || definition?.key || definition?.nodeId || '').trim(),
title: String(definition?.title || '').trim(),
execute: definition?.execute,
}))
.filter((definition) => Number.isFinite(definition.id) && typeof definition.execute === 'function')
.filter((definition) => definition.nodeId && typeof definition.execute === 'function')
.sort((left, right) => {
const leftOrder = Number.isFinite(left.order) ? left.order : left.id;
const rightOrder = Number.isFinite(right.order) ? right.order : right.id;
return leftOrder - rightOrder;
const leftOrder = Number.isFinite(left.displayOrder) ? left.displayOrder : 0;
const rightOrder = Number.isFinite(right.displayOrder) ? right.displayOrder : 0;
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
return left.nodeId.localeCompare(right.nodeId);
});
const byId = new Map(ordered.map((definition) => [definition.id, definition]));
const byId = new Map(ordered.map((definition) => [definition.nodeId, definition]));
function getStepDefinition(step) {
return byId.get(Number(step)) || null;
function getNodeDefinition(nodeId) {
return byId.get(String(nodeId || '').trim()) || null;
}
function getOrderedSteps() {
function getOrderedNodes() {
return ordered.slice();
}
function executeStep(step, state) {
const definition = getStepDefinition(step);
function executeNode(nodeId, state) {
const definition = getNodeDefinition(nodeId);
if (!definition) {
throw new Error(`未知步骤${step}`);
throw new Error(`未知节点${nodeId}`);
}
return definition.execute(state);
}
return {
executeStep,
getOrderedSteps,
getStepDefinition,
executeNode,
getNodeDefinition,
getOrderedNodes,
};
}
return {
createStepRegistry,
createNodeRegistry,
};
});
+5 -4
View File
@@ -5,7 +5,7 @@
const {
addLog,
chrome,
completeStepFromBackground,
completeNodeFromBackground,
ensureContentScriptReadyOnTab,
ensureSignupAuthEntryPageReady,
ensureSignupEntryPageReady,
@@ -152,7 +152,8 @@
try {
return await sendToContentScriptResilient('signup-page', {
type: 'EXECUTE_STEP',
type: 'EXECUTE_NODE',
nodeId: 'submit-signup-email',
step: 2,
source: 'background',
payload,
@@ -391,7 +392,7 @@
skipUrlWait: Boolean(step2Result?.alreadyOnPasswordPage),
});
await completeStepFromBackground(2, {
await completeNodeFromBackground('submit-signup-email', {
accountIdentifierType: 'phone',
accountIdentifier: phoneNumber,
signupPhoneNumber: phoneNumber,
@@ -484,7 +485,7 @@
skipUrlWait: Boolean(step2Result?.alreadyOnPasswordPage),
});
await completeStepFromBackground(2, {
await completeNodeFromBackground('submit-signup-email', {
email: resolvedEmail,
accountIdentifierType: 'email',
accountIdentifier: resolvedEmail,
@@ -99,7 +99,7 @@
const {
addLog = async () => {},
chrome: chromeApi = globalThis.chrome,
completeStepFromBackground,
completeNodeFromBackground,
getErrorMessage = (error) => error?.message || String(error || '未知错误'),
registrationSuccessWaitMs = DEFAULT_REGISTRATION_SUCCESS_WAIT_MS,
sleepWithStop = async (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0))),
@@ -149,7 +149,7 @@
}
await clearCookiesIfEnabled(state);
await addLog('步骤 6:注册成功等待完成,准备继续获取 OAuth 链接并登录。', 'ok');
await completeStepFromBackground(6);
await completeNodeFromBackground('wait-registration-success');
}
return { executeStep6 };