import GuJumpgate snapshot from GitHub
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
(function attachBackgroundStep6(root, factory) {
|
||||
root.MultiPageBackgroundStep6 = factory();
|
||||
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() {
|
||||
const DEFAULT_REGISTRATION_SUCCESS_WAIT_MS = 4000;
|
||||
const LOCAL_CPA_JSON_NO_RT_PANEL_MODE = 'local-cpa-json-no-rt';
|
||||
const LOCAL_CPA_JSON_EXPORT_NODE_ID = 'local-cpa-json-export';
|
||||
const CHATGPT_SESSION_EXPORT_URL = 'https://chatgpt.com/';
|
||||
const STEP6_COOKIE_CLEAR_DOMAINS = [
|
||||
'chatgpt.com',
|
||||
'chat.openai.com',
|
||||
'pay.openai.com',
|
||||
'openai.com',
|
||||
'auth.openai.com',
|
||||
'auth0.openai.com',
|
||||
'accounts.openai.com',
|
||||
'paypal.com',
|
||||
'stripe.com',
|
||||
'checkout.stripe.com',
|
||||
'meiguodizhi.com',
|
||||
'mail-api.yuecheng.shop',
|
||||
'yuecheng.shop',
|
||||
];
|
||||
const STEP6_COOKIE_CLEAR_ORIGINS = [
|
||||
'https://chatgpt.com',
|
||||
'https://chat.openai.com',
|
||||
'https://pay.openai.com',
|
||||
'https://auth.openai.com',
|
||||
'https://auth0.openai.com',
|
||||
'https://accounts.openai.com',
|
||||
'https://openai.com',
|
||||
'https://www.paypal.com',
|
||||
'https://paypal.com',
|
||||
'https://checkout.stripe.com',
|
||||
'https://www.meiguodizhi.com',
|
||||
'https://meiguodizhi.com',
|
||||
'https://mail-api.yuecheng.shop',
|
||||
];
|
||||
|
||||
function normalizeStep6CookieDomain(domain) {
|
||||
return String(domain || '').trim().replace(/^\.+/, '').toLowerCase();
|
||||
}
|
||||
|
||||
function shouldClearStep6Cookie(cookie) {
|
||||
const domain = normalizeStep6CookieDomain(cookie?.domain);
|
||||
if (!domain) return false;
|
||||
return STEP6_COOKIE_CLEAR_DOMAINS.some((target) => (
|
||||
domain === target || domain.endsWith(`.${target}`)
|
||||
));
|
||||
}
|
||||
|
||||
function buildStep6CookieRemovalUrl(cookie) {
|
||||
const host = normalizeStep6CookieDomain(cookie?.domain);
|
||||
const rawPath = String(cookie?.path || '/');
|
||||
const path = rawPath.startsWith('/') ? rawPath : `/${rawPath}`;
|
||||
return `https://${host}${path}`;
|
||||
}
|
||||
|
||||
async function collectStep6Cookies(chromeApi) {
|
||||
if (!chromeApi.cookies?.getAll) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const stores = chromeApi.cookies.getAllCookieStores
|
||||
? await chromeApi.cookies.getAllCookieStores()
|
||||
: [{ id: undefined }];
|
||||
const cookies = [];
|
||||
const seen = new Set();
|
||||
|
||||
for (const store of stores) {
|
||||
const storeId = store?.id;
|
||||
const batch = await chromeApi.cookies.getAll(storeId ? { storeId } : {});
|
||||
for (const cookie of batch || []) {
|
||||
if (!shouldClearStep6Cookie(cookie)) continue;
|
||||
const key = [
|
||||
cookie.storeId || storeId || '',
|
||||
cookie.domain || '',
|
||||
cookie.path || '',
|
||||
cookie.name || '',
|
||||
cookie.partitionKey ? JSON.stringify(cookie.partitionKey) : '',
|
||||
].join('|');
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
cookies.push(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
return cookies;
|
||||
}
|
||||
|
||||
async function removeStep6Cookie(chromeApi, cookie, getErrorMessage) {
|
||||
const details = {
|
||||
url: buildStep6CookieRemovalUrl(cookie),
|
||||
name: cookie.name,
|
||||
};
|
||||
if (cookie.storeId) {
|
||||
details.storeId = cookie.storeId;
|
||||
}
|
||||
if (cookie.partitionKey) {
|
||||
details.partitionKey = cookie.partitionKey;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await chromeApi.cookies.remove(details);
|
||||
return Boolean(result);
|
||||
} catch (error) {
|
||||
console.warn('[MultiPage:step6] remove cookie failed', {
|
||||
domain: cookie?.domain,
|
||||
name: cookie?.name,
|
||||
message: getErrorMessage(error),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function createStep6Executor(deps = {}) {
|
||||
const {
|
||||
addLog = async () => {},
|
||||
buildLocalHelperEndpoint = null,
|
||||
chrome: chromeApi = globalThis.chrome,
|
||||
completeNodeFromBackground,
|
||||
createLocalCliProxyApi = null,
|
||||
ensureContentScriptReadyOnTab = async () => {},
|
||||
getErrorMessage = (error) => error?.message || String(error || '未知错误'),
|
||||
getPanelMode = (state = {}) => String(state?.panelMode || '').trim() || 'cpa',
|
||||
getTabId = async () => null,
|
||||
normalizeHotmailLocalBaseUrl = (value) => String(value || '').trim(),
|
||||
registrationSuccessWaitMs = DEFAULT_REGISTRATION_SUCCESS_WAIT_MS,
|
||||
sessionExportInjectFiles = ['content/utils.js', 'content/operation-delay.js', 'content/plus-checkout.js'],
|
||||
sendToContentScriptResilient = null,
|
||||
sleepWithStop = async (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0))),
|
||||
} = deps;
|
||||
|
||||
function normalizeString(value = '') {
|
||||
return String(value || '').trim();
|
||||
}
|
||||
|
||||
function isLocalCpaJsonNoRtMode(state = {}) {
|
||||
return normalizeString(getPanelMode(state)) === LOCAL_CPA_JSON_NO_RT_PANEL_MODE;
|
||||
}
|
||||
|
||||
function getLocalCliProxyApi() {
|
||||
const factory = createLocalCliProxyApi
|
||||
|| globalThis.MultiPageBackgroundLocalCliProxyApi?.createLocalCliProxyApi
|
||||
|| null;
|
||||
if (typeof factory !== 'function') {
|
||||
throw new Error('本地 CPA JSON 无RT 模块未加载,无法导出认证文件。');
|
||||
}
|
||||
return factory({
|
||||
crypto: globalThis.crypto,
|
||||
fetch: typeof globalThis.fetch === 'function' ? globalThis.fetch.bind(globalThis) : null,
|
||||
sessionToJsonConverter: globalThis.MultiPageSessionToJsonConverter,
|
||||
});
|
||||
}
|
||||
|
||||
async function saveLocalCpaJsonArtifactViaHelper(helperBaseUrl, artifact) {
|
||||
const endpoint = typeof buildLocalHelperEndpoint === 'function'
|
||||
? buildLocalHelperEndpoint(helperBaseUrl, '/save-auth-json')
|
||||
: new URL('/save-auth-json', `${helperBaseUrl.replace(/\/+$/, '')}/`).toString();
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
filePath: artifact.filePath,
|
||||
directoryPath: artifact.directoryPath,
|
||||
content: artifact.jsonText,
|
||||
}),
|
||||
});
|
||||
|
||||
let payload = {};
|
||||
try {
|
||||
payload = await response.json();
|
||||
} catch {
|
||||
payload = {};
|
||||
}
|
||||
|
||||
if (!response.ok || payload?.ok === false) {
|
||||
const helperError = normalizeString(payload?.error);
|
||||
if (/Missing email\/clientId\/refreshToken/i.test(helperError)) {
|
||||
throw new Error('本地 helper 未识别 /save-auth-json,当前运行的 hotmail_helper.py 版本过旧或不是当前项目目录。请停止旧 helper,并从当前 FlowPilot-FlowPilot1.0.2 目录重新启动本地助手。');
|
||||
}
|
||||
throw new Error(helperError || `本地 helper 写入失败(HTTP ${response.status})。`);
|
||||
}
|
||||
|
||||
return {
|
||||
...artifact,
|
||||
filePath: normalizeString(payload?.filePath) || artifact.filePath,
|
||||
};
|
||||
}
|
||||
|
||||
async function openChatGptSessionExportTab(state = {}) {
|
||||
if (chromeApi?.tabs?.create) {
|
||||
const tab = await chromeApi.tabs.create({
|
||||
url: CHATGPT_SESSION_EXPORT_URL,
|
||||
active: false,
|
||||
});
|
||||
const tabId = Number(tab?.id);
|
||||
if (Number.isInteger(tabId) && tabId > 0) {
|
||||
return {
|
||||
source: 'plus-checkout',
|
||||
tabId,
|
||||
temporary: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const fallbackTabId = Number(state?.plusCheckoutTabId || await getTabId('plus-checkout') || await getTabId('signup-page'));
|
||||
if (!Number.isInteger(fallbackTabId) || fallbackTabId <= 0) {
|
||||
throw new Error('未找到可读取 ChatGPT 会话的标签页,无法导出本地 CPA JSON 无RT。');
|
||||
}
|
||||
return {
|
||||
source: 'plus-checkout',
|
||||
tabId: fallbackTabId,
|
||||
temporary: false,
|
||||
};
|
||||
}
|
||||
|
||||
async function closeTemporarySessionExportTab(tabInfo = {}) {
|
||||
if (!tabInfo?.temporary || !Number.isInteger(Number(tabInfo?.tabId)) || !chromeApi?.tabs?.remove) {
|
||||
return;
|
||||
}
|
||||
await chromeApi.tabs.remove(Number(tabInfo.tabId)).catch(() => {});
|
||||
}
|
||||
|
||||
async function readChatGptSessionForExport(state = {}, visibleStep = 7) {
|
||||
if (typeof sendToContentScriptResilient !== 'function') {
|
||||
throw new Error('当前环境缺少 ChatGPT 会话读取通道,无法导出本地 CPA JSON 无RT。');
|
||||
}
|
||||
|
||||
const tabInfo = await openChatGptSessionExportTab(state);
|
||||
try {
|
||||
await ensureContentScriptReadyOnTab(tabInfo.source, tabInfo.tabId, {
|
||||
inject: sessionExportInjectFiles,
|
||||
injectSource: tabInfo.source,
|
||||
timeoutMs: 30000,
|
||||
retryDelayMs: 800,
|
||||
logMessage: `步骤 ${visibleStep}:正在连接 ChatGPT 页面,准备读取当前会话并导出 JSON...`,
|
||||
logStep: visibleStep,
|
||||
logStepKey: LOCAL_CPA_JSON_EXPORT_NODE_ID,
|
||||
});
|
||||
|
||||
const sessionResult = await sendToContentScriptResilient(tabInfo.source, {
|
||||
type: 'PLUS_CHECKOUT_GET_STATE',
|
||||
step: visibleStep,
|
||||
source: 'background',
|
||||
payload: {
|
||||
includeSession: true,
|
||||
includeAccessToken: true,
|
||||
},
|
||||
}, {
|
||||
timeoutMs: 15000,
|
||||
retryDelayMs: 500,
|
||||
logMessage: `步骤 ${visibleStep}:正在等待 ChatGPT 页面返回当前登录会话...`,
|
||||
logStep: visibleStep,
|
||||
logStepKey: LOCAL_CPA_JSON_EXPORT_NODE_ID,
|
||||
});
|
||||
|
||||
if (sessionResult?.error) {
|
||||
throw new Error(sessionResult.error);
|
||||
}
|
||||
return sessionResult;
|
||||
} finally {
|
||||
await closeTemporarySessionExportTab(tabInfo);
|
||||
}
|
||||
}
|
||||
|
||||
async function exportLocalCpaJsonNoRt(state = {}, options = {}) {
|
||||
const visibleStep = Math.max(1, Math.floor(Number(options.visibleStep) || 7));
|
||||
const helperBaseUrl = normalizeHotmailLocalBaseUrl(state.hotmailLocalBaseUrl);
|
||||
const pluginDir = normalizeString(state.localCpaJsonPluginDir);
|
||||
if (!helperBaseUrl) {
|
||||
throw new Error('尚未配置 Hotmail 本地助手地址,请先在侧边栏填写。');
|
||||
}
|
||||
if (!pluginDir) {
|
||||
throw new Error('尚未配置本地插件目录,请先在侧边栏填写。');
|
||||
}
|
||||
|
||||
const sessionResult = await readChatGptSessionForExport(state, visibleStep);
|
||||
const api = getLocalCliProxyApi();
|
||||
const artifact = await api.buildAuthJsonArtifact({
|
||||
pluginDir,
|
||||
relativeAuthDir: state.localCpaJsonRelativeAuthDir,
|
||||
session: sessionResult?.session,
|
||||
accessToken: sessionResult?.accessToken,
|
||||
sessionToken: sessionResult?.session?.sessionToken,
|
||||
email: sessionResult?.email || sessionResult?.session?.user?.email || state?.email,
|
||||
expiresAt: sessionResult?.expiresAt || sessionResult?.session?.expires,
|
||||
accountId: sessionResult?.session?.account?.id,
|
||||
userId: sessionResult?.session?.user?.id,
|
||||
planType: sessionResult?.session?.account?.planType,
|
||||
lastRefresh: '',
|
||||
sourceName: 'SessionToJson Local No RT',
|
||||
});
|
||||
|
||||
for (const warning of Array.isArray(artifact.warnings) ? artifact.warnings : []) {
|
||||
await addLog(`步骤 ${visibleStep}:${warning}`, 'warn');
|
||||
}
|
||||
|
||||
const saved = await saveLocalCpaJsonArtifactViaHelper(helperBaseUrl, artifact);
|
||||
const verifiedStatus = `本地CPA JSON 无RT 已导出:${saved.filePath}`;
|
||||
await addLog(`步骤 ${visibleStep}:${verifiedStatus}`, 'ok');
|
||||
return {
|
||||
verifiedStatus,
|
||||
localCpaJsonFilePath: saved.filePath,
|
||||
};
|
||||
}
|
||||
|
||||
async function clearCookiesIfEnabled(state = {}) {
|
||||
if (!state?.step6CookieCleanupEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!chromeApi?.cookies?.getAll || !chromeApi.cookies?.remove) {
|
||||
await addLog('步骤 6:当前浏览器不支持 cookies API,跳过第六步 Cookies 清理。', 'warn');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await addLog('步骤 6:已开启 Cookies 清理,正在清理 ChatGPT / OpenAI cookies...', 'info');
|
||||
const cookies = await collectStep6Cookies(chromeApi);
|
||||
let removedCount = 0;
|
||||
for (const cookie of cookies) {
|
||||
if (await removeStep6Cookie(chromeApi, cookie, getErrorMessage)) {
|
||||
removedCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (chromeApi.browsingData?.removeCookies) {
|
||||
try {
|
||||
await chromeApi.browsingData.removeCookies({
|
||||
since: 0,
|
||||
origins: STEP6_COOKIE_CLEAR_ORIGINS,
|
||||
});
|
||||
} catch (error) {
|
||||
await addLog(`步骤 6:browsingData 补扫 cookies 失败:${getErrorMessage(error)}`, 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
await addLog(`步骤 6:已清理 ${removedCount} 个 ChatGPT / OpenAI cookies。`, 'ok');
|
||||
} catch (error) {
|
||||
await addLog(`步骤 6:Cookies 清理失败,已跳过并继续后续流程:${getErrorMessage(error)}`, 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
async function executeStep6(state = {}) {
|
||||
const baseWaitMs = Math.max(0, Math.floor(Number(registrationSuccessWaitMs) || 0));
|
||||
const waitMs = baseWaitMs;
|
||||
if (waitMs > 0) {
|
||||
await addLog(`步骤 6:等待 ${Math.round(waitMs / 1000)} 秒,确认注册成功并让页面稳定...`, 'info');
|
||||
await sleepWithStop(waitMs);
|
||||
}
|
||||
await clearCookiesIfEnabled(state);
|
||||
await addLog('步骤 6:注册成功等待完成,注册阶段已结束。', 'ok');
|
||||
await completeNodeFromBackground('wait-registration-success', {});
|
||||
}
|
||||
|
||||
async function executeLocalCpaJsonNoRtExport(state = {}) {
|
||||
if (!isLocalCpaJsonNoRtMode(state)) {
|
||||
throw new Error('当前不是本地CPA JSON 无RT 模式,不能执行无RT导出节点。');
|
||||
}
|
||||
await addLog('步骤 7:Plus Checkout 已完成,等待 5 秒后导出本地 CPA JSON 无RT...', 'info');
|
||||
await sleepWithStop(5000);
|
||||
const completionPayload = await exportLocalCpaJsonNoRt(state, { visibleStep: 7 });
|
||||
await completeNodeFromBackground(LOCAL_CPA_JSON_EXPORT_NODE_ID, completionPayload);
|
||||
}
|
||||
|
||||
return {
|
||||
executeLocalCpaJsonNoRtExport,
|
||||
executeStep6,
|
||||
};
|
||||
}
|
||||
|
||||
return { createStep6Executor };
|
||||
});
|
||||
Reference in New Issue
Block a user