/** * Claude WebView Injection Scripts * * JavaScript code injected into Claude WebView to: * 1. Show consent popup for user approval * 2. Fetch conversations from Claude API * 3. Extract ONLY user messages (not AI responses) * 4. Track progress during export * 5. Send data back to React Native * * @reference ChatGPT implementation: src/utils/webviewScripts/chatgpt.ts */ /** * Consent Popup Script * Shows user consent UI before exporting conversations */ export declare const CLAUDE_CONSENT_POPUP_SCRIPT = "\n(function() {\n try {\n const existing = document.getElementById('onairos-consent-overlay');\n if (existing) existing.remove();\n \n const overlay = document.createElement('div');\n overlay.id = 'onairos-consent-overlay';\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.7);\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n `;\n \n const popup = document.createElement('div');\n popup.style.cssText = `\n background: white;\n border-radius: 16px;\n padding: 32px;\n max-width: 400px;\n width: 90%;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n `;\n \n popup.innerHTML = `\n
\n
\uD83E\uDDE0
\n

\n Export Conversations?\n

\n

\n Allow Onairos to export your top 10 Claude conversations for analysis?\n

\n
\n \n \n
\n
\n `;\n \n overlay.appendChild(popup);\n document.body.appendChild(overlay);\n \n document.getElementById('onairos-deny').onclick = () => {\n overlay.remove();\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CLAUDE_CONSENT',\n status: 'denied'\n }));\n };\n \n document.getElementById('onairos-allow').onclick = () => {\n document.getElementById('onairos-allow').innerHTML = '\u23F3 Starting...';\n document.getElementById('onairos-allow').disabled = true;\n \n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CLAUDE_CONSENT',\n status: 'allowed'\n }));\n \n setTimeout(() => overlay.remove(), 500);\n };\n } catch (error) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CLAUDE_CONSENT',\n status: 'error',\n message: error.message\n }));\n }\n})();\ntrue;\n"; /** * Export Script - Claude conversation extraction * * Claude API Flow: * 1. Get organizations list from /api/organizations * 2. Get conversations from /api/organizations/{orgId}/chat_conversations * 3. Fetch each conversation detail * 4. Extract user messages */ export declare const CLAUDE_EXPORT_SCRIPT = "\n(function() {\n try {\n if (!document.body) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CONVERSATION_EXPORT_ERROR',\n message: 'Page not ready'\n }));\n return;\n }\n\n const baseUrl = 'https://claude.ai';\n console.log('\uD83D\uDE80 [CLAUDE] Starting extraction...');\n\n (async () => {\n try {\n // STEP 1: Get organization ID\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'EXPORT_PROGRESS',\n stage: 'fetching_org',\n progress: 2,\n message: 'Getting organization info...'\n }));\n\n console.log('[CLAUDE] Fetching organization info...');\n \n let organizationId = null;\n try {\n const orgResponse = await fetch(baseUrl + '/api/organizations', {\n method: 'GET',\n credentials: 'include',\n headers: {\n 'Accept': 'application/json',\n 'Referer': baseUrl\n }\n });\n \n console.log('[CLAUDE] Organizations response status:', orgResponse.status);\n \n if (orgResponse.ok) {\n const orgData = await orgResponse.json();\n // Get first organization (usually the personal one)\n if (Array.isArray(orgData) && orgData.length > 0) {\n organizationId = orgData[0].uuid || orgData[0].id;\n console.log('[CLAUDE] Got organizationId:', organizationId);\n }\n }\n } catch (orgError) {\n console.warn('[CLAUDE] Organization fetch failed:', orgError.message);\n }\n\n if (!organizationId) {\n // Try alternate method - check cookies or page data\n console.log('[CLAUDE] Trying alternate org ID detection...');\n const cookieMatch = document.cookie.match(/lastActiveOrg=([^;]+)/);\n if (cookieMatch) {\n organizationId = cookieMatch[1];\n console.log('[CLAUDE] Got org ID from cookie:', organizationId);\n }\n }\n\n if (!organizationId) {\n throw new Error('Could not determine Claude organization ID');\n }\n\n // STEP 2: Request conversations\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'EXPORT_PROGRESS',\n stage: 'fetching_list',\n progress: 5,\n message: 'Fetching conversation list...'\n }));\n\n const headers = {\n 'Accept': 'application/json',\n 'Referer': baseUrl,\n 'Content-Type': 'application/json'\n };\n \n console.log('[CLAUDE] Fetching conversations...');\n \n const listResponse = await fetch(baseUrl + '/api/organizations/' + organizationId + '/chat_conversations', {\n method: 'GET',\n credentials: 'include',\n headers: headers\n });\n\n console.log('[CLAUDE] Conversations response:', listResponse.status);\n\n if (!listResponse.ok) {\n throw new Error('HTTP ' + listResponse.status + ': Failed to fetch conversations');\n }\n\n const listData = await listResponse.json();\n // Claude returns conversations directly as array or in a data property\n const conversations = listData.data || listData || [];\n \n // Take only first 10 conversations\n const limitedConversations = conversations.slice(0, 10);\n \n console.log('[CLAUDE] Found', limitedConversations.length, 'conversations (limited to 10)');\n\n if (limitedConversations.length === 0) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CONVERSATION_EXPORT_COMPLETE',\n status: 'success',\n data: {\n conversations: [],\n total_conversations: 0,\n total_messages: 0\n },\n success: true,\n message: 'Connected! No conversations found.'\n }));\n return;\n }\n\n // STEP 3: Fetch each conversation\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'EXPORT_PROGRESS',\n progress: 10,\n message: 'Processing 0/' + limitedConversations.length + ' conversations'\n }));\n\n const userMessages = [];\n\n for (let i = 0; i < limitedConversations.length; i++) {\n const conv = limitedConversations[i];\n const convId = conv.uuid || conv.id;\n const progressPercent = 10 + ((i / limitedConversations.length) * 80);\n\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'EXPORT_PROGRESS',\n stage: 'processing_conversation',\n current: i + 1,\n total: limitedConversations.length,\n progress: Math.round(progressPercent),\n message: 'Processing ' + (i + 1) + '/' + limitedConversations.length + ' conversations'\n }));\n\n try {\n // Delay between requests\n if (i > 0) {\n await new Promise(resolve => setTimeout(resolve, 300));\n }\n \n const detailResponse = await fetch(baseUrl + '/api/organizations/' + organizationId + '/chat_conversations/' + convId, {\n method: 'GET',\n credentials: 'include',\n headers: headers\n });\n\n if (detailResponse.ok) {\n const convData = await detailResponse.json();\n const messages = [];\n\n // Extract user messages from chat_messages array\n const chatMessages = convData.chat_messages || convData.messages || [];\n \n for (const msg of chatMessages) {\n // Claude uses 'human' for user messages, 'assistant' for AI\n if (msg.sender === 'human' || msg.role === 'user') {\n const content = msg.text || msg.content || '';\n if (typeof content === 'string' && content.trim()) {\n messages.push(content.trim());\n } else if (Array.isArray(content)) {\n // Handle content array format\n for (const part of content) {\n if (part.type === 'text' && part.text) {\n messages.push(part.text.trim());\n }\n }\n }\n }\n }\n\n if (messages.length > 0) {\n userMessages.push({\n conversation_id: convId,\n title: conv.name || conv.title || 'Untitled',\n user_messages: messages\n });\n console.log('[CLAUDE] \u2705 Conversation', (i + 1) + '/' + limitedConversations.length + ':', conv.name || conv.title || 'Untitled', '(' + messages.length + ' msgs)');\n }\n } else {\n console.warn('[CLAUDE] \u26A0\uFE0F Conversation', convId, 'returned', detailResponse.status);\n }\n } catch (convError) {\n console.warn('[CLAUDE] \u26A0\uFE0F Failed conversation', convId + ':', convError.message);\n }\n }\n\n const totalMessages = userMessages.reduce((sum, c) => sum + c.user_messages.length, 0);\n\n console.log('\u2705 [CLAUDE] Extraction complete:', totalMessages, 'messages from', userMessages.length, 'conversations');\n\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CONVERSATION_EXPORT_COMPLETE',\n status: 'success',\n data: {\n conversations: userMessages,\n total_conversations: userMessages.length,\n total_messages: totalMessages\n },\n success: true\n }));\n\n } catch (error) {\n console.error('\u274C [CLAUDE] Export failed:', error.message);\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CONVERSATION_EXPORT_ERROR',\n status: 'error',\n message: 'Fetch failed: ' + error.message,\n success: false\n }));\n }\n })();\n\n } catch (error) {\n console.error('\u274C [CLAUDE] Init error:', error.message);\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'CONVERSATION_EXPORT_ERROR',\n status: 'error',\n message: 'Script error: ' + error.message,\n success: false\n }));\n }\n})();\ntrue;\n"; export declare const CLAUDE_SUCCESS_SCRIPT = "\n(function() {\n console.log('\u2705 Claude export successful!');\n})();\ntrue;\n"; export declare const CLAUDE_ERROR_SCRIPT = "\n(function() {\n console.log('\u274C Claude export failed!');\n})();\ntrue;\n"; //# sourceMappingURL=claude.d.ts.map