tag.')
+ } catch (error) {
+ console.error('[content] Failed to parse JSON from tag for access token:', error)
+ }
+ } else {
+ console.warn('[content] tag not found or empty for access token on /api/auth/session.')
+ }
+ } else {
+ console.debug('[content] Not on /api/auth/session page, fetching token from API endpoint.')
+ try {
+ const resp = await fetch('https://chatgpt.com/api/auth/session')
+ if (resp.ok) {
+ data = await resp.json()
+ console.debug('[content] Fetched access token data from API endpoint.')
+ } else {
+ console.warn(
+ `[content] Failed to fetch access token, status: ${resp.status}`,
+ await resp.text(),
+ )
+ }
+ } catch (error) {
+ console.error('[content] Error fetching access token from API:', error)
+ }
}
- }
- await Browser.runtime.sendMessage({
- type: 'SET_CHATGPT_TAB',
- data: {},
- })
-
- registerPortListener(async (session, port) => {
- if (isUsingChatgptWebModel(session)) {
- const accessToken = await getChatGptAccessToken()
- await generateAnswersWithChatgptWebApi(port, session.question, session, accessToken)
+ if (data?.accessToken) {
+ await setAccessToken(data.accessToken)
+ console.log('[content] ChatGPT Access token has been set successfully from page data.')
+ } else {
+ console.warn('[content] No access token found in page data or fetch response.')
}
- })
+ } catch (error) {
+ console.error('[content] Error in overwriteAccessToken:', error)
+ }
}
async function getClaudeSessionKey() {
- return Browser.runtime.sendMessage({
- type: 'GET_COOKIE',
- data: { url: 'https://claude.ai/', name: 'sessionKey' },
- })
+ console.debug('[content] getClaudeSessionKey called.')
+ try {
+ const sessionKey = await Browser.runtime.sendMessage({
+ type: 'GET_COOKIE',
+ data: { url: 'https://claude.ai/', name: 'sessionKey' },
+ })
+ console.debug('[content] Claude session key from background:', sessionKey ? 'found' : 'not found')
+ return sessionKey
+ } catch (error) {
+ console.error('[content] Error in getClaudeSessionKey sending message:', error)
+ return null
+ }
}
async function prepareForJumpBackNotification() {
- if (
- location.hostname === 'chatgpt.com' &&
- document.querySelector('button[data-testid=login-button]')
- ) {
- console.log('chatgpt not logged in')
- return
- }
-
- const url = new URL(window.location.href)
- if (url.searchParams.has('chatgptbox_notification')) {
- if (location.hostname === 'claude.ai' && !(await getClaudeSessionKey())) {
- console.log('claude not logged in')
-
- await new Promise((resolve) => {
- const timer = setInterval(async () => {
- const token = await getClaudeSessionKey()
- if (token) {
- clearInterval(timer)
- resolve()
- }
- }, 500)
- })
+ console.log('[content] Initializing jump back notification.')
+ try {
+ if (
+ location.hostname === 'chatgpt.com' &&
+ document.querySelector('button[data-testid=login-button]')
+ ) {
+ console.log('[content] ChatGPT login button found, user not logged in. Skipping jump back.')
+ return
}
- if (location.hostname === 'kimi.moonshot.cn' && !window.localStorage.refresh_token) {
- console.log('kimi not logged in')
- setTimeout(() => {
- document.querySelectorAll('button').forEach((button) => {
- if (button.textContent === '立即登录') {
- button.click()
+ const url = new URL(window.location.href)
+ if (url.searchParams.has('chatgptbox_notification')) {
+ console.log('[content] chatgptbox_notification param found in URL.')
+
+ if (location.hostname === 'claude.ai') {
+ console.debug('[content] On claude.ai, checking login status.')
+ let claudeSession = await getClaudeSessionKey()
+ if (!claudeSession) {
+ console.log('[content] Claude session key not found, waiting for it...')
+ let promiseSettled = false
+ let timerId = null
+ let timeoutId = null
+ const cleanup = () => {
+ if (timerId) clearInterval(timerId)
+ if (timeoutId) clearTimeout(timeoutId)
}
- })
- }, 1000)
-
- await new Promise((resolve) => {
- const timer = setInterval(() => {
- const token = window.localStorage.refresh_token
- if (token) {
- setUserConfig({
- kimiMoonShotRefreshToken: token,
- })
- clearInterval(timer)
- resolve()
+
+ await new Promise((resolve, reject) => {
+ timerId = setInterval(async () => {
+ if (promiseSettled) {
+ console.warn('[content] Promise already settled but Claude interval still running. This indicates a potential cleanup issue.');
+ cleanup()
+ return
+ }
+ try {
+ claudeSession = await getClaudeSessionKey()
+ if (claudeSession) {
+ if (!promiseSettled) {
+ promiseSettled = true
+ cleanup()
+ console.log('[content] Claude session key found after waiting.')
+ resolve()
+ }
+ }
+ } catch (err) {
+ console.error('[content] Error polling for Claude session key:', err)
+ const errMsg = err.message.toLowerCase();
+ if ((errMsg.includes('network') || errMsg.includes('permission')) && !promiseSettled) {
+ promiseSettled = true;
+ cleanup();
+ reject(new Error(`Failed to get Claude session key due to: ${err.message}`));
+ }
+ }
+ }, 500)
+
+ timeoutId = setTimeout(() => {
+ if (!promiseSettled) {
+ promiseSettled = true
+ cleanup()
+ console.warn('[content] Timed out waiting for Claude session key.')
+ reject(new Error('Timed out waiting for Claude session key.'))
+ }
+ }, 30000)
+ }).catch((err) => {
+ console.error('[content] Failed to get Claude session key for jump back notification:', err)
+ return
+ })
+ } else {
+ console.log('[content] Claude session key found immediately.')
+ }
+ }
+
+ if (location.hostname === 'kimi.moonshot.cn') {
+ console.debug('[content] On kimi.moonshot.cn, checking login status.')
+ if (!window.localStorage.refresh_token) {
+ console.log('[content] Kimi refresh token not found, attempting to trigger login.')
+ setTimeout(() => {
+ try {
+ document.querySelectorAll('button').forEach((button) => {
+ if (button.textContent === '立即登录') {
+ console.log('[content] Clicking Kimi login button.')
+ button.click()
+ }
+ })
+ } catch (err_click) {
+ console.error('[content] Error clicking Kimi login button:', err_click)
+ }
+ }, 1000)
+
+ let promiseSettled = false
+ let timerId = null
+ let timeoutId = null
+ const cleanup = () => {
+ if (timerId) clearInterval(timerId)
+ if (timeoutId) clearTimeout(timeoutId)
}
- }, 500)
- })
- }
- const div = document.createElement('div')
- document.body.append(div)
- render(
- ,
- div,
- )
+ await new Promise((resolve, reject) => {
+ timerId = setInterval(async () => {
+ if (promiseSettled) {
+ console.warn('[content] Promise already settled but Kimi interval still running. This indicates a potential cleanup issue.');
+ cleanup()
+ return
+ }
+ try {
+ const token = window.localStorage.refresh_token
+ if (token) {
+ if (!promiseSettled) {
+ promiseSettled = true
+ cleanup()
+ console.log('[content] Kimi refresh token found after waiting.')
+ await setUserConfig({ kimiMoonShotRefreshToken: token })
+ console.log('[content] Kimi refresh token saved to config.')
+ resolve()
+ }
+ }
+ } catch (err_set) {
+ console.error('[content] Error setting Kimi refresh token from polling:', err_set)
+ const errMsg = err_set.message.toLowerCase();
+ if ((errMsg.includes('network') || errMsg.includes('storage')) && !promiseSettled) { // Example error check
+ promiseSettled = true;
+ cleanup();
+ reject(new Error(`Failed to process Kimi token: ${err_set.message}`));
+ }
+ }
+ }, 500)
+
+ timeoutId = setTimeout(() => {
+ if (!promiseSettled) {
+ promiseSettled = true
+ cleanup()
+ console.warn('[content] Timed out waiting for Kimi refresh token.')
+ reject(new Error('Timed out waiting for Kimi refresh token.'))
+ }
+ }, 30000)
+ }).catch((err) => {
+ console.error('[content] Failed to get Kimi refresh token for jump back notification:', err)
+ return
+ })
+ } else {
+ console.log('[content] Kimi refresh token found in localStorage.')
+ await setUserConfig({ kimiMoonShotRefreshToken: window.localStorage.refresh_token })
+ }
+ }
+
+ console.log('[content] Rendering WebJumpBackNotification.')
+ const div = document.createElement('div')
+ document.body.append(div)
+ render(
+ ,
+ div,
+ )
+ console.log('[content] WebJumpBackNotification rendered.')
+ } else {
+ console.debug('[content] No chatgptbox_notification param in URL.')
+ }
+ } catch (error) {
+ console.error('[content] Error in prepareForJumpBackNotification:', error)
}
}
async function run() {
- await getPreferredLanguageKey().then((lang) => {
- changeLanguage(lang)
- })
- Browser.runtime.onMessage.addListener(async (message) => {
- if (message.type === 'CHANGE_LANG') {
- const data = message.data
- changeLanguage(data.lang)
+ console.log('[content] Script run started.')
+ try {
+ await getPreferredLanguageKey().then((lang) => {
+ console.log(`[content] Setting language to: ${lang}`)
+ changeLanguage(lang)
+ }).catch(err => console.error('[content] Error setting preferred language:', err))
+
+ Browser.runtime.onMessage.addListener(async (message) => {
+ console.debug('[content] Received runtime message:', message)
+ try {
+ if (message.type === 'CHANGE_LANG') {
+ console.log('[content] Processing CHANGE_LANG message:', message.data)
+ changeLanguage(message.data.lang)
+ }
+ } catch (error) {
+ console.error('[content] Error in global runtime.onMessage listener:', error, message)
+ }
+ })
+
+ await overwriteAccessToken()
+ await manageChatGptTabState()
+
+ Browser.storage.onChanged.addListener(async (changes, areaName) => {
+ console.debug('[content] Storage changed:', changes, 'in area:', areaName)
+ try {
+ if (areaName === 'local' && (changes.userConfig || changes.config)) {
+ console.log(
+ '[content] User config changed in storage, re-evaluating ChatGPT tab state.',
+ )
+ await manageChatGptTabState()
+ }
+ } catch (error) {
+ console.error('[content] Error in storage.onChanged listener:', error)
+ }
+ })
+
+ await prepareForSelectionTools()
+ await prepareForSelectionToolsTouch()
+ await prepareForStaticCard()
+ await prepareForRightClickMenu()
+ await prepareForJumpBackNotification()
+
+ console.log('[content] Script run completed successfully.')
+ } catch (error) {
+ console.error('[content] Error in run function:', error)
+ }
+}
+
+async function manageChatGptTabState() {
+ console.debug('[content] manageChatGptTabState called. Current location:', location.href)
+ try {
+ if (location.hostname !== 'chatgpt.com' || location.pathname === '/auth/login') {
+ console.debug(
+ '[content] Not on main chatgpt.com page, skipping manageChatGptTabState logic.',
+ )
+ return
}
- })
- await overwriteAccessToken()
- await prepareForForegroundRequests()
+ const userConfig = await getUserConfig()
+ console.debug('[content] User config in manageChatGptTabState:', userConfig)
+ const isThisTabDesignatedForChatGptWeb = chatgptWebModelKeys.some((model) =>
+ getApiModesStringArrayFromConfig(userConfig, true).includes(model),
+ )
+ console.debug(
+ '[content] Is this tab designated for ChatGPT Web:',
+ isThisTabDesignatedForChatGptWeb,
+ )
+
+ if (isThisTabDesignatedForChatGptWeb) {
+ if (location.pathname === '/') {
+ console.debug('[content] On chatgpt.com root path.')
+ const input = document.querySelector('#prompt-textarea')
+ if (input && input.value === '') {
+ console.log('[content] Manipulating #prompt-textarea for focus.')
+ input.value = ' '
+ input.dispatchEvent(new Event('input', { bubbles: true }))
+ setTimeout(() => {
+ if (input && input.value === ' ') {
+ input.value = ''
+ input.dispatchEvent(new Event('input', { bubbles: true }))
+ console.debug('[content] #prompt-textarea manipulation complete.')
+ } else if (!input) {
+ console.warn('[content] #prompt-textarea no longer available in setTimeout callback.');
+ }
+ }, 300)
+ } else {
+ console.debug(
+ '[content] #prompt-textarea not found, not empty (value: "'+ input?.value +'"), or not on root path for manipulation.',
+ )
+ }
+ }
- prepareForSelectionTools()
- prepareForSelectionToolsTouch()
- prepareForStaticCard()
- prepareForRightClickMenu()
- prepareForJumpBackNotification()
+ console.log('[content] Sending SET_CHATGPT_TAB message.')
+ await Browser.runtime.sendMessage({
+ type: 'SET_CHATGPT_TAB',
+ data: {},
+ })
+ console.log('[content] SET_CHATGPT_TAB message sent successfully.')
+ } else {
+ console.log('[content] This tab is NOT configured for ChatGPT Web model processing.')
+ }
+ } catch (error) {
+ console.error('[content] Error in manageChatGptTabState:', error)
+ }
+}
+
+if (!window.__chatGPTBoxPortListenerRegistered) {
+ try {
+ if (location.hostname === 'chatgpt.com' && location.pathname !== '/auth/login') {
+ console.log('[content] Attempting to register port listener for chatgpt.com.')
+ registerPortListener(async (session, port) => {
+ console.debug(
+ `[content] Port listener callback triggered. Session:`,
+ session,
+ `Port:`,
+ port.name,
+ )
+ try {
+ if (isUsingChatgptWebModel(session)) {
+ console.log(
+ '[content] Session is for ChatGPT Web Model, processing request for question:',
+ session.question,
+ )
+ const accessToken = await getChatGptAccessToken()
+ if (!accessToken) {
+ console.warn('[content] No ChatGPT access token available for web API call.')
+ port.postMessage({ error: 'Missing ChatGPT access token.' })
+ return
+ }
+ await generateAnswersWithChatgptWebApi(port, session.question, session, accessToken)
+ console.log('[content] generateAnswersWithChatgptWebApi call completed.')
+ } else {
+ console.debug(
+ '[content] Session is not for ChatGPT Web Model, skipping processing in this listener.',
+ )
+ }
+ } catch (e) {
+ console.error('[content] Error in port listener callback:', e, 'Session:', session)
+ try {
+ port.postMessage({ error: e.message || 'An unexpected error occurred in content script port listener.' })
+ } catch (postError) {
+ console.error('[content] Error sending error message back via port:', postError)
+ }
+ }
+ })
+ console.log('[content] Generic port listener registered successfully for chatgpt.com pages.')
+ window.__chatGPTBoxPortListenerRegistered = true
+ } else {
+ console.debug(
+ '[content] Not on chatgpt.com or on login page, skipping port listener registration.',
+ )
+ }
+ } catch (error) {
+ console.error('[content] Error registering global port listener:', error)
+ }
+} else {
+ console.log('[content] Port listener already registered, skipping.')
}
run()