UNPKG

supapup

Version:

⚡ Lightning-fast MCP browser dev tool. Navigate → Get instant structured data. No screenshots needed! Puppeteer: 📸 → CSS selectors → JS eval. Supapup: semantic IDs ready to use. 10x faster, 90% fewer tokens.

173 lines (172 loc) 7.98 kB
import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; /** * DevTools Agent Page Generator * * This class generates agent-friendly page representations using Chrome DevTools Protocol. * It creates a unified text representation that matches what humans see, with proper * form detection and bulk form fill capabilities. */ export class DevToolsAgentPageGenerator { page; cdpSession = null; constructor(page) { this.page = page; } /** * Generate agent page with form detection */ async generateAgentPage(enhanced) { try { // Debug to file (commented out for production) // const fs = await import('fs'); const debugLog = (msg) => { // const timestamp = new Date().toISOString(); // fs.appendFileSync('/tmp/supapup-debug.log', `${timestamp} ${msg}\n`); // Uncomment above lines to enable file logging for debugging }; debugLog('🔧 Starting agent page generation...'); // Setup CDP session if not already available await this.setupCDP(); // Generate the unified representation in browser context with hover detection debugLog('🔧 Evaluating generator script in browser context...'); let result; try { debugLog('About to call page.evaluate...'); result = await this.page.evaluate(() => { console.log('🔧 Inside browser evaluate - checking generator...'); const generator = window.__DEVTOOLS_AGENT_GENERATOR__; if (!generator) { console.error('❌ Generator not found on window object'); console.log('Available window properties:', Object.keys(window).filter(k => k.includes('AGENT') || k.includes('DEVTOOLS'))); throw new Error('DevTools agent generator not available - script injection failed'); } console.log('✅ Generator found, calling generate()...'); try { const genResult = generator.generate(); console.log('✅ Generate completed, result type:', typeof genResult); console.log('✅ Result has content:', typeof genResult?.content); console.log('✅ Content length:', genResult?.content?.length); // Explicitly return a serializable object const returnValue = { content: genResult.content || '', elements: genResult.elements || [], actions: genResult.actions || [], title: genResult.title || '', url: genResult.url || '', _debug: { hasContent: !!genResult.content, contentLength: genResult.content?.length || 0, elementsCount: genResult.elements?.length || 0 } }; console.log('🔧 Returning serialized result:', returnValue._debug); return returnValue; } catch (genError) { console.error('❌ Generator.generate() failed:', genError); console.error('❌ Error stack:', genError.stack); throw new Error(`Generator execution failed: ${genError.message}`); } }); } catch (evalError) { debugLog(`page.evaluate error: ${evalError.message}`); throw evalError; } debugLog('🔧 Browser evaluation completed'); debugLog(`🔧 Result type: ${typeof result}`); debugLog(`🔧 Result is null/undefined: ${result === null || result === undefined}`); debugLog(`🔧 Result has content property: ${'content' in (result || {})}`); if (!result) { throw new Error('Generator returned null/undefined result'); } if (!result.content) { console.error('❌ Result object:', JSON.stringify(result, null, 2)); throw new Error('Generator result missing content property'); } console.log('✅ Agent page generation successful'); return result; } catch (error) { // Try to log to file even on error (commented out for production) // try { // const fs = await import('fs'); // fs.appendFileSync('/tmp/supapup-debug.log', `${new Date().toISOString()} ❌ Error: ${error.message}\n${error.stack}\n`); // } catch (e) {} console.error('❌ DevTools agent page generation failed:', error); console.error('❌ Error stack:', error.stack); throw new Error(`Agent page generation failed: ${error.message}`); } } /** * Setup Chrome DevTools Protocol session */ async setupCDP() { try { // Create CDP session if not exists if (!this.cdpSession) { this.cdpSession = await this.page.target().createCDPSession(); } // Enable required domains await this.cdpSession.send('Runtime.enable'); await this.cdpSession.send('DOM.enable'); await this.cdpSession.send('CSS.enable'); // Inject the generator script into browser context const script = this.getGeneratorScript(); console.log('🔧 Injecting DevTools agent generator script...'); // Always inject the script for the current page try { await this.page.evaluate(script); console.log('✅ Script evaluated successfully'); } catch (scriptError) { console.error('❌ Script evaluation failed:', scriptError); throw new Error(`Script evaluation failed: ${scriptError}`); } // Also set it up for future navigations await this.page.evaluateOnNewDocument(script); // Verify the script was injected successfully const windowProps = await this.page.evaluate(() => { return { hasGenerator: typeof window.__DEVTOOLS_AGENT_GENERATOR__ !== 'undefined', hasAgentPage: typeof window.__AGENT_PAGE__ !== 'undefined' }; }); if (!windowProps.hasGenerator || !windowProps.hasAgentPage) { throw new Error(`Script injection verification failed. Window properties: ${JSON.stringify(windowProps)}`); } console.log('✅ DevTools agent generator injected successfully'); } catch (error) { console.error('❌ CDP setup failed:', error); throw new Error(`CDP setup failed: ${error}`); } } /** * Load the external script file for better maintainability */ getGeneratorScript() { // ES module compatible path resolution const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const scriptPath = path.join(__dirname, 'devtools-agent-page-script.js'); return fs.readFileSync(scriptPath, 'utf8'); } /** * Cleanup CDP session */ async cleanup() { try { if (this.cdpSession) { await this.cdpSession.detach(); this.cdpSession = null; } } catch (error) { console.error('CDP cleanup error:', error); } } }