Skip to main content
Glama
extract-from-existing-browser.js•14.6 kB
#!/usr/bin/env node /** * Extract Authentication from Existing Browser Session v1.0.0 * Connect to existing Chromium browser with active Composer session * * Purpose: Extract localStorage data from already-open browser session * Status: UTILITY - One-time extraction for direct API configuration * * @version 1.0.0 (January 15, 2025) */ import { chromium } from 'playwright'; import fs from 'fs'; import path from 'path'; class ExistingBrowserExtractor { constructor() { this.extractedData = null; this.configPath = path.join(process.cwd(), 'config', 'direct-api-auth.json'); this.envPath = path.join(process.cwd(), 'config', 'direct-api.env'); } /** * Connect to existing browser and extract auth data */ async extractFromExistingBrowser() { console.log('šŸ” Existing Browser Authentication Extractor v1.0.0'); console.log('Purpose: Extract auth data from active Composer session'); console.log('Status: Current v5.2.0 system preserved\n'); let browser = null; let context = null; let page = null; try { // Common CDP endpoints for various Chromium-based browsers const cdpEndpoints = [ 'http://localhost:9222', 'http://127.0.0.1:9222', 'http://localhost:9333', 'http://127.0.0.1:9333', 'http://localhost:9515', 'http://127.0.0.1:9515' ]; console.log('šŸ”Œ Attempting to connect to existing browser...'); let connected = false; let connectionError = null; // Try each CDP endpoint for (const endpoint of cdpEndpoints) { try { console.log(` Trying ${endpoint}...`); browser = await chromium.connectOverCDP(endpoint); console.log(` āœ… Connected successfully to ${endpoint}`); connected = true; break; } catch (error) { connectionError = error.message; // Continue to next endpoint } } if (!connected) { console.log('\nāŒ Could not connect to existing browser.'); console.log('\nšŸ“‹ Manual Instructions to Enable Connection:'); console.log(' 1. Close all Chromium/Chrome browsers'); console.log(' 2. Start Chromium with remote debugging:'); console.log(' macOS: /Applications/Chromium.app/Contents/MacOS/Chromium --remote-debugging-port=9222'); console.log(' OR: /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=9222'); console.log(' 3. Navigate to https://composer.euconquisto.com and log in'); console.log(' 4. Run this script again'); console.log('\n Last error:', connectionError); return null; } // Get all browser contexts const contexts = browser.contexts(); console.log(`šŸ“‘ Found ${contexts.length} browser context(s)`); // Find context with Composer page let composerPage = null; for (const ctx of contexts) { const pages = ctx.pages(); for (const p of pages) { const url = p.url(); console.log(` Checking page: ${url}`); if (url.includes('composer.euconquisto.com')) { composerPage = p; console.log(' āœ… Found Composer page!'); break; } } if (composerPage) break; } if (!composerPage) { console.log('\nāŒ No Composer page found in browser'); console.log(' Please navigate to https://composer.euconquisto.com'); return null; } console.log('\nšŸ” Extracting authentication data from localStorage...'); const authData = await composerPage.evaluate(() => { console.log('=== EXISTING BROWSER AUTH EXTRACTION v1.0.0 ==='); const activeProject = localStorage.getItem('rdp-composer-active-project'); const userData = localStorage.getItem('rdp-composer-user-data'); console.log('Raw activeProject present:', !!activeProject); console.log('Raw userData present:', !!userData); if (!activeProject) { return { success: false, error: 'Active project data not found in localStorage' }; } if (!userData) { return { success: false, error: 'User data not found in localStorage' }; } let projectData, userDataParsed; try { projectData = JSON.parse(activeProject); userDataParsed = JSON.parse(userData); } catch (e) { return { success: false, error: 'Failed to parse localStorage data: ' + e.message }; } if (!projectData.uid) { return { success: false, error: 'Project UID not found in active project data' }; } if (!userDataParsed.access_token) { return { success: false, error: 'Access token not found in user data' }; } if (!projectData.connectors || !Array.isArray(projectData.connectors)) { return { success: false, error: 'Connectors array not found or invalid in project data' }; } // Extract all necessary data const result = { success: true, extractedAt: new Date().toISOString(), accessToken: userDataParsed.access_token, tokenType: userDataParsed.token_type || 'Bearer', projectUid: projectData.uid, projectName: projectData.name || 'Unknown Project', connectors: projectData.connectors.map(c => ({ uid: c.uid, name: c.name, type: c.type, permissions: c.permissions || [] })), userInfo: { uid: userDataParsed.uid, name: userDataParsed.name, email: userDataParsed.email } }; console.log('Extraction successful:', { projectUid: result.projectUid, projectName: result.projectName, connectorsCount: result.connectors.length, hasAccessToken: !!result.accessToken, tokenType: result.tokenType, tokenLength: result.accessToken.length, userUid: result.userInfo.uid, userName: result.userInfo.name }); return result; }); this.extractedData = authData; if (authData.success) { console.log('\nāœ… Authentication data extracted successfully!'); console.log(` Project: ${authData.projectName} (${authData.projectUid})`); console.log(` User: ${authData.userInfo.name} (${authData.userInfo.email})`); console.log(` Token Type: ${authData.tokenType}`); console.log(` Token Length: ${authData.accessToken.length} characters`); console.log(` Connectors: ${authData.connectors.length} available`); // List connectors console.log('\nšŸ“” Available Connectors:'); authData.connectors.forEach((connector, index) => { console.log(` ${index + 1}. ${connector.name || 'Unnamed'} (${connector.uid})`); if (connector.permissions.length > 0) { console.log(` Permissions: ${connector.permissions.join(', ')}`); } }); await this.saveConfiguration(); await this.generateEnvironmentFile(); await this.generateMCPConfig(); } else { console.log('\nāŒ Failed to extract authentication data:'); console.log(` Error: ${authData.error}`); } } catch (error) { console.error('āŒ Extraction error:', error.message); this.extractedData = { success: false, error: error.message }; } finally { // Note: We don't close the browser since it's the user's active session console.log('\n✨ Browser session remains open (not closing existing session)'); } return this.extractedData; } /** * Save configuration to JSON file */ async saveConfiguration() { if (!this.extractedData || !this.extractedData.success) { return; } try { // Create config directory if it doesn't exist const configDir = path.dirname(this.configPath); if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } // Prepare config data (remove sensitive full tokens from saved file) const configData = { extractedAt: this.extractedData.extractedAt, tokenType: this.extractedData.tokenType, projectUid: this.extractedData.projectUid, projectName: this.extractedData.projectName, connectors: this.extractedData.connectors, userInfo: this.extractedData.userInfo, // Store token preview only in JSON file accessTokenPreview: this.extractedData.accessToken.substring(0, 50) + '...', note: 'Full access token stored in environment file for security' }; fs.writeFileSync(this.configPath, JSON.stringify(configData, null, 2)); console.log(`\nšŸ’¾ Configuration saved to: ${this.configPath}`); } catch (error) { console.error('āŒ Failed to save configuration:', error.message); } } /** * Generate environment file with full authentication data */ async generateEnvironmentFile() { if (!this.extractedData || !this.extractedData.success) { return; } try { const envContent = [ '# EuConquisto Composer Direct API Authentication', '# Generated on: ' + new Date().toISOString(), '# Source: Active Composer session localStorage extraction', '', '# Full JWT Access Token', `EUCONQUISTO_ACCESS_TOKEN="${this.extractedData.accessToken}"`, '', '# Token Type (usually Bearer)', `EUCONQUISTO_TOKEN_TYPE="${this.extractedData.tokenType}"`, '', '# Project Information', `EUCONQUISTO_PROJECT_UID="${this.extractedData.projectUid}"`, `EUCONQUISTO_PROJECT_NAME="${this.extractedData.projectName}"`, '', '# Connectors (JSON format)', `EUCONQUISTO_CONNECTORS='${JSON.stringify(this.extractedData.connectors)}'`, '', '# User Information (optional)', `EUCONQUISTO_USER_UID="${this.extractedData.userInfo.uid}"`, `EUCONQUISTO_USER_NAME="${this.extractedData.userInfo.name}"`, `EUCONQUISTO_USER_EMAIL="${this.extractedData.userInfo.email}"`, '', '# Usage:', '# source config/direct-api.env', '# export $(grep -v "^#" config/direct-api.env | xargs)', '' ].join('\n'); fs.writeFileSync(this.envPath, envContent); console.log(`šŸ’¾ Environment file saved to: ${this.envPath}`); console.log(' Use: source config/direct-api.env'); } catch (error) { console.error('āŒ Failed to generate environment file:', error.message); } } /** * Generate MCP configuration for Claude Desktop */ async generateMCPConfig() { if (!this.extractedData || !this.extractedData.success) { return; } try { const mcpConfig = { "mcpServers": { "euconquisto-composer-direct": { "command": "node", "args": [ "--max-old-space-size=4096", path.resolve(process.cwd(), "dist", "index.js") ], "env": { "NODE_ENV": "production", "EUCONQUISTO_ACCESS_TOKEN": this.extractedData.accessToken, "EUCONQUISTO_TOKEN_TYPE": this.extractedData.tokenType, "EUCONQUISTO_PROJECT_UID": this.extractedData.projectUid, "EUCONQUISTO_CONNECTORS": JSON.stringify(this.extractedData.connectors), "EUCONQUISTO_USER_UID": this.extractedData.userInfo.uid, "EUCONQUISTO_USER_NAME": this.extractedData.userInfo.name } } } }; const mcpConfigPath = path.join(process.cwd(), 'config', 'claude-desktop-config-direct-api.json'); fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2)); console.log(`šŸ’¾ MCP configuration saved to: ${mcpConfigPath}`); console.log(' Add this to your Claude Desktop configuration'); } catch (error) { console.error('āŒ Failed to generate MCP configuration:', error.message); } } /** * Output summary */ outputSummary() { console.log('\n' + '='.repeat(70)); console.log('šŸ“‹ AUTHENTICATION EXTRACTION SUMMARY'); console.log('='.repeat(70)); if (this.extractedData && this.extractedData.success) { console.log('\nāœ… EXTRACTION SUCCESSFUL'); console.log(` Project: ${this.extractedData.projectName}`); console.log(` User: ${this.extractedData.userInfo.name}`); console.log(` Connectors: ${this.extractedData.connectors.length} available`); console.log(` Extracted: ${this.extractedData.extractedAt}`); console.log('\nšŸ“ FILES CREATED:'); console.log(` Configuration: ${this.configPath}`); console.log(` Environment: ${this.envPath}`); console.log(` MCP Config: config/claude-desktop-config-direct-api.json`); console.log('\nšŸŽÆ NEXT STEPS:'); console.log(' 1. Load environment: source config/direct-api.env'); console.log(' 2. Test direct API: node src/tools/direct-api-client.js'); console.log(' 3. Update Claude Desktop config if desired'); console.log(' 4. Consider replacing JWT redirect server with direct API calls'); console.log('\nšŸ” SECURITY NOTE:'); console.log(' • Full JWT token stored in config/direct-api.env'); console.log(' • Add config/direct-api.env to .gitignore'); console.log(' • Token expires - re-extract when needed'); } else { console.log('\nāŒ EXTRACTION FAILED'); if (this.extractedData) { console.log(` Error: ${this.extractedData.error}`); } } console.log('\nšŸ“Œ CURRENT PROJECT STATUS:'); console.log(' v5.2.0 FULLY OPERATIONAL system is preserved and unchanged'); console.log(' This extraction enables optional direct API approach'); console.log('\n' + '='.repeat(70) + '\n'); } } // Execute extraction if run directly if (import.meta.url === `file://${process.argv[1]}`) { const extractor = new ExistingBrowserExtractor(); extractor.extractFromExistingBrowser() .then(() => { extractor.outputSummary(); }) .catch(error => { console.error('āŒ Extraction process error:', error.message); process.exit(1); }); } export { ExistingBrowserExtractor };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rkm097git/euconquisto-composer-mcp-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server