Refactor workflow auto-run to node graph
This commit is contained in:
+34
-10
@@ -20,7 +20,7 @@ if (document.documentElement.getAttribute(SIGNUP_PAGE_LISTENER_SENTINEL) !== '1'
|
||||
// Listen for commands from Background
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (
|
||||
message.type === 'EXECUTE_STEP'
|
||||
message.type === 'EXECUTE_NODE'
|
||||
|| message.type === 'FILL_CODE'
|
||||
|| message.type === 'STEP8_FIND_AND_CLICK'
|
||||
|| message.type === 'STEP8_GET_STATE'
|
||||
@@ -46,6 +46,7 @@ if (document.documentElement.getAttribute(SIGNUP_PAGE_LISTENER_SENTINEL) !== '1'
|
||||
sendResponse({ ok: true, ...(result || {}) });
|
||||
}).catch(err => {
|
||||
const reportedStep = Number(message.payload?.visibleStep) || message.step;
|
||||
const reportedNodeId = resolveCommandNodeId(message);
|
||||
if (isStopError(err)) {
|
||||
if (reportedStep) {
|
||||
log(`步骤 ${reportedStep || 8}:已被用户停止。`, 'warn');
|
||||
@@ -61,7 +62,7 @@ if (document.documentElement.getAttribute(SIGNUP_PAGE_LISTENER_SENTINEL) !== '1'
|
||||
}
|
||||
|
||||
if (reportedStep) {
|
||||
reportError(reportedStep, err.message);
|
||||
reportError(reportedNodeId || reportedStep, err.message);
|
||||
}
|
||||
sendResponse({ error: err.message });
|
||||
});
|
||||
@@ -72,17 +73,40 @@ if (document.documentElement.getAttribute(SIGNUP_PAGE_LISTENER_SENTINEL) !== '1'
|
||||
console.log('[MultiPage:signup-page] 消息监听已存在,跳过重复注册');
|
||||
}
|
||||
|
||||
const SIGNUP_PAGE_NODE_HANDLERS = Object.freeze({
|
||||
'submit-signup-email': (payload) => step2_clickRegister(payload),
|
||||
'fill-password': (payload) => step3_fillEmailPassword(payload),
|
||||
'fill-profile': (payload) => step5_fillNameBirthday(payload),
|
||||
'oauth-login': (payload) => step6_login(payload),
|
||||
'confirm-oauth': (_payload) => step8_findAndClick(),
|
||||
});
|
||||
|
||||
function resolveCommandNodeId(message = {}) {
|
||||
const directNodeId = String(message.nodeId || message.payload?.nodeId || '').trim();
|
||||
if (directNodeId) {
|
||||
return directNodeId;
|
||||
}
|
||||
const visibleStep = Number(message.payload?.visibleStep || message.step) || 0;
|
||||
if (visibleStep === 4) return 'fetch-signup-code';
|
||||
if (visibleStep === 8 || visibleStep === 11) return 'fetch-login-code';
|
||||
if (visibleStep === 9 || visibleStep === 12) return 'confirm-oauth';
|
||||
if (visibleStep === 7 || visibleStep === 10) return 'oauth-login';
|
||||
if (visibleStep === 5) return 'fill-profile';
|
||||
if (visibleStep === 3) return 'fill-password';
|
||||
if (visibleStep === 2) return 'submit-signup-email';
|
||||
return '';
|
||||
}
|
||||
|
||||
async function handleCommand(message) {
|
||||
switch (message.type) {
|
||||
case 'EXECUTE_STEP':
|
||||
switch (message.step) {
|
||||
case 2: return await step2_clickRegister(message.payload);
|
||||
case 3: return await step3_fillEmailPassword(message.payload);
|
||||
case 5: return await step5_fillNameBirthday(message.payload);
|
||||
case 7: return await step6_login(message.payload);
|
||||
case 9: return await step8_findAndClick();
|
||||
default: throw new Error(`signup-page.js 不处理步骤 ${message.step}`);
|
||||
case 'EXECUTE_NODE': {
|
||||
const nodeId = String(message.nodeId || message.payload?.nodeId || '').trim();
|
||||
const handler = SIGNUP_PAGE_NODE_HANDLERS[nodeId];
|
||||
if (!handler) {
|
||||
throw new Error(`signup-page.js 不处理节点 ${nodeId}`);
|
||||
}
|
||||
return await handler(message.payload || {});
|
||||
}
|
||||
case 'FILL_CODE':
|
||||
// Step 4 = signup code, Step 7 = login code (same handler)
|
||||
return await fillVerificationCode(message.step, message.payload);
|
||||
|
||||
@@ -14,23 +14,23 @@ if (document.documentElement.getAttribute(SUB2API_PANEL_LISTENER_SENTINEL) !== '
|
||||
document.documentElement.setAttribute(SUB2API_PANEL_LISTENER_SENTINEL, '1');
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === 'EXECUTE_STEP' || message.type === 'REQUEST_OAUTH_URL') {
|
||||
if (message.type === 'EXECUTE_NODE' || message.type === 'REQUEST_OAUTH_URL') {
|
||||
resetStopState();
|
||||
const handler = message.type === 'REQUEST_OAUTH_URL'
|
||||
? requestOAuthUrl(message.payload)
|
||||
: handleStep(message.step, message.payload);
|
||||
: handleNode(message.nodeId || message.payload?.nodeId, message.payload);
|
||||
handler.then((result) => {
|
||||
sendResponse({ ok: true, ...(result || {}) });
|
||||
}).catch((err) => {
|
||||
if (isStopError(err)) {
|
||||
if (message.step) {
|
||||
log('已被用户停止。', 'warn', { step: message.step });
|
||||
if (message.payload?.visibleStep || message.step) {
|
||||
log('已被用户停止。', 'warn', { step: message.payload?.visibleStep || message.step });
|
||||
}
|
||||
sendResponse({ stopped: true, error: err.message });
|
||||
return;
|
||||
}
|
||||
if (message.step) {
|
||||
reportError(message.step, err.message);
|
||||
if (message.nodeId || message.payload?.nodeId) {
|
||||
reportError(message.nodeId || message.payload?.nodeId, err.message);
|
||||
}
|
||||
sendResponse({ error: err.message });
|
||||
});
|
||||
@@ -76,6 +76,16 @@ async function handleStep(step, payload = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleNode(nodeId, payload = {}) {
|
||||
const normalizedNodeId = String(nodeId || '').trim();
|
||||
switch (normalizedNodeId) {
|
||||
case 'platform-verify':
|
||||
return step9_submitOpenAiCallback(payload);
|
||||
default:
|
||||
throw new Error(`sub2api-panel.js 不处理节点 ${normalizedNodeId}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function requestOAuthUrl(payload = {}) {
|
||||
return step1_generateOpenAiAuthUrl(payload, { report: false });
|
||||
}
|
||||
@@ -665,9 +675,10 @@ async function step9_submitOpenAiCallback(payload = {}) {
|
||||
|
||||
const verifiedStatus = `SUB2API 已创建账号 #${createdAccount?.id || 'unknown'}`;
|
||||
log(verifiedStatus, 'ok', { step: visibleStep, stepKey: 'platform-verify' });
|
||||
reportComplete(visibleStep, {
|
||||
reportComplete('platform-verify', {
|
||||
localhostUrl: callback.url,
|
||||
verifiedStatus,
|
||||
visibleStep,
|
||||
});
|
||||
openAccountsPageSoon(origin);
|
||||
}
|
||||
|
||||
+117
-20
@@ -274,6 +274,35 @@ function normalizeLogStep(value) {
|
||||
return step > 0 ? step : null;
|
||||
}
|
||||
|
||||
const DEFAULT_OPENAI_NODE_BY_STEP = Object.freeze({
|
||||
1: 'open-chatgpt',
|
||||
2: 'submit-signup-email',
|
||||
3: 'fill-password',
|
||||
4: 'fetch-signup-code',
|
||||
5: 'fill-profile',
|
||||
6: 'wait-registration-success',
|
||||
7: 'oauth-login',
|
||||
8: 'fetch-login-code',
|
||||
9: 'confirm-oauth',
|
||||
10: 'platform-verify',
|
||||
11: 'fetch-login-code',
|
||||
12: 'confirm-oauth',
|
||||
13: 'platform-verify',
|
||||
});
|
||||
|
||||
function resolveReportNodeId(stepOrNodeId, data = {}) {
|
||||
const explicitNodeId = String(data?.nodeId || data?.nodeKey || '').trim();
|
||||
if (explicitNodeId) {
|
||||
return explicitNodeId;
|
||||
}
|
||||
const directNodeId = String(stepOrNodeId || '').trim();
|
||||
if (directNodeId && !/^\d+$/.test(directNodeId)) {
|
||||
return directNodeId;
|
||||
}
|
||||
const step = normalizeLogStep(stepOrNodeId || data?.step || data?.visibleStep);
|
||||
return step ? DEFAULT_OPENAI_NODE_BY_STEP[step] || '' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a log message to Side Panel via Background.
|
||||
* @param {string} message
|
||||
@@ -323,30 +352,65 @@ function reportReady() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report step completion.
|
||||
* @param {number} step
|
||||
* @param {Object} data - Step output data
|
||||
* Report node completion.
|
||||
* @param {string|number} stepOrNodeId
|
||||
* @param {Object} data - Node output data
|
||||
*/
|
||||
function reportComplete(step, data = {}) {
|
||||
console.log(LOG_PREFIX, `步骤 ${step} 已完成`, data);
|
||||
log('已成功完成', 'ok', { step });
|
||||
function reportComplete(stepOrNodeId, data = {}) {
|
||||
const nodeId = resolveReportNodeId(stepOrNodeId, data);
|
||||
const step = normalizeLogStep(stepOrNodeId || data?.step || data?.visibleStep);
|
||||
console.log(LOG_PREFIX, `节点 ${nodeId || stepOrNodeId} 已完成`, data);
|
||||
log('已成功完成', 'ok', { step, stepKey: nodeId });
|
||||
const message = {
|
||||
type: 'STEP_COMPLETE',
|
||||
type: 'NODE_COMPLETE',
|
||||
source: getRuntimeScriptSource(),
|
||||
step,
|
||||
payload: data,
|
||||
nodeId,
|
||||
payload: {
|
||||
...(data || {}),
|
||||
...(nodeId ? { nodeId } : {}),
|
||||
...(step ? { step } : {}),
|
||||
},
|
||||
error: null,
|
||||
};
|
||||
Promise.resolve(chrome.runtime.sendMessage(message))
|
||||
.then((response) => {
|
||||
console.log(LOG_PREFIX, `STEP_COMPLETE sent successfully for step ${step}`, {
|
||||
console.log(LOG_PREFIX, `NODE_COMPLETE sent successfully for node ${nodeId || stepOrNodeId}`, {
|
||||
response,
|
||||
url: location.href,
|
||||
payloadKeys: Object.keys(data || {}),
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(LOG_PREFIX, `STEP_COMPLETE send failed for step ${step}`, err?.message || err, {
|
||||
console.error(LOG_PREFIX, `NODE_COMPLETE send failed for node ${nodeId || stepOrNodeId}`, err?.message || err, {
|
||||
url: location.href,
|
||||
payloadKeys: Object.keys(data || {}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reportNodeComplete(nodeId, data = {}) {
|
||||
const normalizedNodeId = String(nodeId || '').trim();
|
||||
console.log(LOG_PREFIX, `节点 ${normalizedNodeId} 已完成`, data);
|
||||
const message = {
|
||||
type: 'NODE_COMPLETE',
|
||||
source: getRuntimeScriptSource(),
|
||||
nodeId: normalizedNodeId,
|
||||
payload: {
|
||||
...(data || {}),
|
||||
nodeId: normalizedNodeId,
|
||||
},
|
||||
error: null,
|
||||
};
|
||||
Promise.resolve(chrome.runtime.sendMessage(message))
|
||||
.then((response) => {
|
||||
console.log(LOG_PREFIX, `NODE_COMPLETE sent successfully for node ${normalizedNodeId}`, {
|
||||
response,
|
||||
url: location.href,
|
||||
payloadKeys: Object.keys(data || {}),
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(LOG_PREFIX, `NODE_COMPLETE send failed for node ${normalizedNodeId}`, err?.message || err, {
|
||||
url: location.href,
|
||||
payloadKeys: Object.keys(data || {}),
|
||||
});
|
||||
@@ -354,29 +418,62 @@ function reportComplete(step, data = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report step error.
|
||||
* @param {number} step
|
||||
* Report node error.
|
||||
* @param {string|number} stepOrNodeId
|
||||
* @param {string} errorMessage
|
||||
*/
|
||||
function reportError(step, errorMessage) {
|
||||
console.error(LOG_PREFIX, `步骤 ${step} 失败: ${errorMessage}`);
|
||||
function reportError(stepOrNodeId, errorMessage) {
|
||||
const nodeId = resolveReportNodeId(stepOrNodeId);
|
||||
const step = normalizeLogStep(stepOrNodeId);
|
||||
console.error(LOG_PREFIX, `节点 ${nodeId || stepOrNodeId} 失败: ${errorMessage}`);
|
||||
const message = {
|
||||
type: 'STEP_ERROR',
|
||||
type: 'NODE_ERROR',
|
||||
source: getRuntimeScriptSource(),
|
||||
step,
|
||||
payload: {},
|
||||
nodeId,
|
||||
payload: {
|
||||
...(nodeId ? { nodeId } : {}),
|
||||
...(step ? { step } : {}),
|
||||
},
|
||||
error: errorMessage,
|
||||
};
|
||||
Promise.resolve(chrome.runtime.sendMessage(message))
|
||||
.then((response) => {
|
||||
console.log(LOG_PREFIX, `STEP_ERROR sent successfully for step ${step}`, {
|
||||
console.log(LOG_PREFIX, `NODE_ERROR sent successfully for node ${nodeId || stepOrNodeId}`, {
|
||||
response,
|
||||
url: location.href,
|
||||
errorMessage,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(LOG_PREFIX, `STEP_ERROR send failed for step ${step}`, err?.message || err, {
|
||||
console.error(LOG_PREFIX, `NODE_ERROR send failed for node ${nodeId || stepOrNodeId}`, err?.message || err, {
|
||||
url: location.href,
|
||||
errorMessage,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reportNodeError(nodeId, errorMessage) {
|
||||
const normalizedNodeId = String(nodeId || '').trim();
|
||||
console.error(LOG_PREFIX, `节点 ${normalizedNodeId} 失败: ${errorMessage}`);
|
||||
const message = {
|
||||
type: 'NODE_ERROR',
|
||||
source: getRuntimeScriptSource(),
|
||||
nodeId: normalizedNodeId,
|
||||
payload: {
|
||||
nodeId: normalizedNodeId,
|
||||
},
|
||||
error: errorMessage,
|
||||
};
|
||||
Promise.resolve(chrome.runtime.sendMessage(message))
|
||||
.then((response) => {
|
||||
console.log(LOG_PREFIX, `NODE_ERROR sent successfully for node ${normalizedNodeId}`, {
|
||||
response,
|
||||
url: location.href,
|
||||
errorMessage,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(LOG_PREFIX, `NODE_ERROR send failed for node ${normalizedNodeId}`, err?.message || err, {
|
||||
url: location.href,
|
||||
errorMessage,
|
||||
});
|
||||
|
||||
+19
-9
@@ -1,4 +1,4 @@
|
||||
// content/vps-panel.js — Content script for CPA panel (steps 7, 10 / OAuth URL request)
|
||||
// content/vps-panel.js — Content script for CPA panel (OAuth URL request / platform verification node)
|
||||
// Injected on: CPA panel (user-configured URL)
|
||||
//
|
||||
// Actual DOM structure (after login click):
|
||||
@@ -39,12 +39,12 @@ if (document.documentElement.getAttribute(VPS_PANEL_LISTENER_SENTINEL) !== '1')
|
||||
|
||||
// Listen for commands from Background
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === 'EXECUTE_STEP' || message.type === 'REQUEST_OAUTH_URL') {
|
||||
if (message.type === 'EXECUTE_NODE' || message.type === 'REQUEST_OAUTH_URL') {
|
||||
resetStopState();
|
||||
const startedAt = Date.now();
|
||||
const actionLabel = message.type === 'REQUEST_OAUTH_URL'
|
||||
? 'REQUEST_OAUTH_URL'
|
||||
: `EXECUTE_STEP received for step ${message.step}`;
|
||||
: `EXECUTE_NODE received for node ${message.nodeId || message.payload?.nodeId || ''}`;
|
||||
console.log(LOG_PREFIX, actionLabel, {
|
||||
url: location.href,
|
||||
payloadKeys: Object.keys(message.payload || {}),
|
||||
@@ -52,7 +52,7 @@ if (document.documentElement.getAttribute(VPS_PANEL_LISTENER_SENTINEL) !== '1')
|
||||
});
|
||||
const handler = message.type === 'REQUEST_OAUTH_URL'
|
||||
? requestOAuthUrl(message.payload)
|
||||
: handleStep(message.step, message.payload);
|
||||
: handleNode(message.nodeId || message.payload?.nodeId, message.payload);
|
||||
handler.then((result) => {
|
||||
console.log(LOG_PREFIX, `${actionLabel} resolved after ${Date.now() - startedAt}ms`, {
|
||||
url: location.href,
|
||||
@@ -65,14 +65,14 @@ if (document.documentElement.getAttribute(VPS_PANEL_LISTENER_SENTINEL) !== '1')
|
||||
snapshot: getVpsPanelSnapshot(),
|
||||
});
|
||||
if (isStopError(err)) {
|
||||
if (message.step) {
|
||||
log('已被用户停止。', 'warn', { step: message.step });
|
||||
if (message.payload?.visibleStep || message.step) {
|
||||
log('已被用户停止。', 'warn', { step: message.payload?.visibleStep || message.step });
|
||||
}
|
||||
sendResponse({ stopped: true, error: err.message });
|
||||
return;
|
||||
}
|
||||
if (message.step) {
|
||||
reportError(message.step, err.message);
|
||||
if (message.nodeId || message.payload?.nodeId) {
|
||||
reportError(message.nodeId || message.payload?.nodeId, err.message);
|
||||
}
|
||||
sendResponse({ error: err.message });
|
||||
});
|
||||
@@ -95,6 +95,16 @@ async function handleStep(step, payload) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleNode(nodeId, payload = {}) {
|
||||
const normalizedNodeId = String(nodeId || '').trim();
|
||||
switch (normalizedNodeId) {
|
||||
case 'platform-verify':
|
||||
return await step9_vpsVerify(payload);
|
||||
default:
|
||||
throw new Error(`vps-panel.js 不处理节点 ${normalizedNodeId}`);
|
||||
}
|
||||
}
|
||||
|
||||
function isVisibleElement(el) {
|
||||
if (!el) return false;
|
||||
const style = window.getComputedStyle(el);
|
||||
@@ -1078,5 +1088,5 @@ async function step9_vpsVerify(payload) {
|
||||
|
||||
const verifiedStatus = await waitForExactSuccessBadge(STEP9_SUCCESS_BADGE_TIMEOUT_MS, visibleStep);
|
||||
log(verifiedStatus, 'ok', { step: visibleStep, stepKey: 'platform-verify' });
|
||||
reportComplete(visibleStep, { localhostUrl, verifiedStatus });
|
||||
reportComplete('platform-verify', { localhostUrl, verifiedStatus, visibleStep });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user