// background/steps/sync-cpa-session.js — Step 10: Sync current ChatGPT session to CPA (function attachBackgroundCpaSessionSync(root, factory) { root.MultiPageBackgroundCpaSessionSync = factory(); })(typeof self !== 'undefined' ? self : globalThis, function createBackgroundCpaSessionSyncModule() { const CHATGPT_SESSION_URL = 'https://chatgpt.com/'; const CHATGPT_SESSION_ENDPOINT = 'https://chatgpt.com/api/auth/session'; function createCpaSessionSyncExecutor(deps = {}) { const { addLog = async () => {}, chrome = null, completeStepFromBackground = async () => {}, createCpaApi = null, fetchImpl = (...args) => fetch(...args), getPanelMode = (state) => (state?.panelMode === 'sub2api' ? 'sub2api' : 'cpa'), getTabId = async () => null, sleepWithStop = sleep, throwIfStopped = () => {}, waitForTabComplete = async () => null, } = deps; let cpaApi = null; function getApi() { if (cpaApi) return cpaApi; const factory = createCpaApi || self.MultiPageBackgroundCpaApi?.createCpaApi; if (typeof factory !== 'function') { throw new Error('CPA 接口模块未加载,无法同步当前 ChatGPT 会话。'); } cpaApi = factory({ addLog, fetchImpl }); return cpaApi; } async function executeStep10(state = {}) { throwIfStopped(); await addLog('步骤 10:正在准备同步 ChatGPT session 到 CPA...'); const mode = getPanelMode(state); if (mode === 'sub2api') { await addLog('步骤 10:当前为 SUB2API 模式,跳过 CPA session 同步。', 'warn'); await completeStepFromBackground(10, { cpaSyncSkipped: true, cpaSyncSkipReason: 'sub2api-mode', }); return; } if (!normalizeString(state.vpsUrl)) { throw new Error('步骤 10:尚未配置 CPA 地址,请先在侧边栏填写。'); } if (!normalizeString(state.vpsPassword)) { throw new Error('步骤 10:尚未配置 CPA 管理密钥,请先在侧边栏填写。'); } await addLog('步骤 10:正在读取 ChatGPT 当前登录 session...'); const sessionState = await readCurrentChatGptSession(); throwIfStopped(); await addLog('步骤 10:已读取 ChatGPT session,正在生成 CPA auth JSON 并同步...'); const result = await getApi().importCurrentChatGptSession({ ...state, session: sessionState.session, accessToken: sessionState.accessToken, }, { logLabel: '步骤 10', timeoutMs: 120000, importTimeoutMs: 120000, }); await completeStepFromBackground(10, result); } async function readCurrentChatGptSession() { try { return await fetchChatGptSessionFromBackground(); } catch (error) { await addLog(`步骤 10:后台读取 ChatGPT session 失败,准备打开页面上下文重试:${error.message}`, 'warn'); } return fetchChatGptSessionFromPage(); } async function fetchChatGptSessionFromBackground() { const response = await fetchImpl(CHATGPT_SESSION_ENDPOINT, { credentials: 'include', headers: { Accept: 'application/json', }, }); const text = await response.text(); let session = null; try { session = text ? JSON.parse(text) : null; } catch { throw new Error(`Session 响应不是有效 JSON:${text.slice(0, 300)}`); } if (!response.ok) { throw new Error(`Session 请求失败,HTTP ${response.status}: ${JSON.stringify(session)}`); } return normalizeSessionResult({ session, accessToken: session?.accessToken }, '后台'); } async function fetchChatGptSessionFromPage() { if (!chrome?.tabs || !chrome?.scripting?.executeScript) { throw new Error('当前环境无法在 ChatGPT 页面上下文读取 session。'); } const tabId = await resolveChatGptSessionTabId(); if (!tabId) { throw new Error('未找到可读取 ChatGPT session 的标签页。'); } await waitForTabComplete(tabId, { timeoutMs: 30000, retryDelayMs: 300 }); await sleepWithStop(1000); const results = await chrome.scripting.executeScript({ target: { tabId }, func: fetchSessionInChatGptPage, }); const result = results?.[0]?.result; if (!result || result.error) { throw new Error(result?.error || '页面上下文读取 ChatGPT session 失败。'); } return normalizeSessionResult(result, '页面'); } async function resolveChatGptSessionTabId() { const registeredSignupTabId = await getTabId('signup-page').catch(() => null); const registeredSignupTab = await getChatGptTabIfReadable(registeredSignupTabId); if (registeredSignupTab?.id) return registeredSignupTab.id; const activeTabs = await chrome.tabs.query({ active: true, currentWindow: true }).catch(() => []); const activeMatch = pickPreferredChatGptTab(activeTabs); if (activeMatch?.id) return activeMatch.id; const chatGptTabs = await chrome.tabs.query({ url: ['https://chatgpt.com/*'] }).catch(() => []); const existingMatch = pickPreferredChatGptTab(chatGptTabs); if (existingMatch?.id) return existingMatch.id; await addLog('步骤 10:未找到 ChatGPT 页面,正在打开 chatgpt.com 以读取 session...', 'info'); const created = await chrome.tabs.create({ url: CHATGPT_SESSION_URL, active: true }); return created?.id || null; } async function getChatGptTabIfReadable(tabId) { const numericTabId = Number(tabId) || 0; if (!numericTabId || !chrome?.tabs?.get) return null; const tab = await chrome.tabs.get(numericTabId).catch(() => null); return tab?.id && isChatGptSessionUrl(tab.url) ? tab : null; } function pickPreferredChatGptTab(tabs = []) { return (Array.isArray(tabs) ? tabs : []) .filter((tab) => Number.isInteger(tab?.id) && isChatGptSessionUrl(tab.url)) .sort((left, right) => { const activeDiff = Number(Boolean(right.active)) - Number(Boolean(left.active)); if (activeDiff) return activeDiff; return (Number(right.lastAccessed) || 0) - (Number(left.lastAccessed) || 0); })[0] || null; } function isChatGptSessionUrl(url = '') { try { const parsed = new URL(String(url || '')); return /^https?:$/i.test(parsed.protocol) && parsed.hostname.toLowerCase() === 'chatgpt.com'; } catch { return false; } } function normalizeSessionResult(result = {}, label = 'ChatGPT') { const session = result?.session && typeof result.session === 'object' && !Array.isArray(result.session) ? result.session : null; const accessToken = normalizeString(result?.accessToken || session?.accessToken); if (!session && !accessToken) { throw new Error(`${label} 未返回有效的 ChatGPT session 或 accessToken。`); } if (!accessToken) { throw new Error(`${label} Session 响应中没有 accessToken。`); } return { session, accessToken }; } return { executeStep10, fetchChatGptSessionFromBackground, isChatGptSessionUrl, }; } async function fetchSessionInChatGptPage() { try { const response = await fetch('/api/auth/session', { credentials: 'include', headers: { Accept: 'application/json', }, }); const text = await response.text(); let session = null; try { session = text ? JSON.parse(text) : null; } catch { return { error: `Session 响应不是有效 JSON:${text.slice(0, 300)}` }; } if (!response.ok) { return { error: `Session 请求失败,HTTP ${response.status}: ${JSON.stringify(session)}` }; } return { session, accessToken: session?.accessToken || '', }; } catch (error) { return { error: error?.message || String(error || '页面上下文读取 ChatGPT session 失败。') }; } } function normalizeString(value = '') { return String(value || '').trim(); } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } return { createCpaSessionSyncExecutor, }; });