#!/usr/bin/env node /** * Verification script for builder message sending to OpenCode * This script validates that all components for message sending are correct */ const fs = require('fs'); const path = require('path'); console.log('╔══════════════════════════════════════════════════════════════╗'); console.log('║ Builder → OpenCode Message Flow Verification ║'); console.log('╚══════════════════════════════════════════════════════════════╝\n'); const rootDir = path.join(__dirname, '..'); let allChecks = true; let checkCount = 0; let passCount = 0; function check(condition, description) { checkCount++; if (condition) { passCount++; console.log(`✓ ${description}`); return true; } else { allChecks = false; console.log(`✗ ${description}`); return false; } } // Load files console.log('Loading files...\n'); const builderJsPath = path.join(rootDir, 'chat/public/builder.js'); const serverJsPath = path.join(rootDir, 'chat/server.js'); const builderHtmlPath = path.join(rootDir, 'chat/public/builder.html'); let builderJs, serverJs, builderHtml; try { builderJs = fs.readFileSync(builderJsPath, 'utf8'); check(true, 'Loaded builder.js'); } catch (e) { check(false, `Failed to load builder.js: ${e.message}`); process.exit(1); } try { serverJs = fs.readFileSync(serverJsPath, 'utf8'); check(true, 'Loaded server.js'); } catch (e) { check(false, `Failed to load server.js: ${e.message}`); process.exit(1); } try { builderHtml = fs.readFileSync(builderHtmlPath, 'utf8'); check(true, 'Loaded builder.html'); } catch (e) { check(false, `Failed to load builder.html: ${e.message}`); process.exit(1); } console.log('\n--- File Syntax Validation ---\n'); // Check syntax of JS files try { new (require('vm').Script)(builderJs, { filename: 'builder.js' }); check(true, 'builder.js syntax is valid'); } catch (e) { check(false, `builder.js has syntax error: ${e.message}`); } try { new (require('vm').Script)(serverJs, { filename: 'server.js' }); check(true, 'server.js syntax is valid'); } catch (e) { check(false, `server.js has syntax error: ${e.message}`); } console.log('\n--- Builder.js Message Sending ---\n'); // Check executeBuild function check( builderJs.includes('async function executeBuild(planContent)'), 'executeBuild function exists' ); check( builderJs.includes('await api(`/api/sessions/${state.currentSessionId}/messages`'), 'executeBuild sends to correct API endpoint' ); check( builderJs.includes("cli: 'opencode'"), 'executeBuild sets cli to "opencode"' ); check( builderJs.includes('isProceedWithBuild: true'), 'executeBuild sets isProceedWithBuild flag' ); check( builderJs.includes('planContent: planContent'), 'executeBuild includes planContent in payload' ); check( builderJs.includes('streamMessage(state.currentSessionId, response.message.id)'), 'executeBuild starts streaming after message creation' ); // Check redoProceedWithBuild function check( builderJs.includes('async function redoProceedWithBuild(planContent, model)'), 'redoProceedWithBuild function exists' ); // Check sendMessage function check( builderJs.includes('async function sendMessage()'), 'sendMessage function exists' ); check( builderJs.includes('await api(`/api/sessions/${state.currentSessionId}/messages`'), 'sendMessage sends to correct API endpoint' ); console.log('\n--- Server.js Message Handling ---\n'); // Check route matching check( serverJs.includes('const messageMatch = pathname.match') && serverJs.includes('api') && serverJs.includes('sessions') && serverJs.includes('messages'), 'Server has message route matcher' ); check( serverJs.includes('return handleNewMessage(req, res, messageMatch[1], userId)'), 'Server routes to handleNewMessage' ); // Check handleNewMessage function check( serverJs.includes('async function handleNewMessage(req, res, sessionId, userId)'), 'handleNewMessage function exists' ); check( serverJs.includes('const content = sanitizeMessage(body.content'), 'handleNewMessage extracts and sanitizes content' ); check( serverJs.includes('const cli = normalizeCli(body.cli || session.cli)'), 'handleNewMessage extracts CLI' ); check( serverJs.includes('session.messages.push(message)'), 'handleNewMessage adds message to session' ); check( serverJs.includes('queueMessage(sessionId, message)'), 'handleNewMessage queues message' ); console.log('\n--- Message Processing ---\n'); // Check processMessage function check( serverJs.includes('async function processMessage(sessionId, message)'), 'processMessage function exists' ); check( serverJs.includes('const opencodeResult = await sendToOpencodeWithFallback'), 'processMessage calls sendToOpencodeWithFallback' ); // Check sendToOpencodeWithFallback function check( serverJs.includes('async function sendToOpencodeWithFallback'), 'sendToOpencodeWithFallback function exists' ); check( serverJs.includes('const result = await sendToOpencode'), 'sendToOpencodeWithFallback calls sendToOpencode' ); console.log('\n--- OpenCode Integration ---\n'); // Check sendToOpencode function check( serverJs.includes('async function sendToOpencode({ session, model, content, message, cli, streamCallback, opencodeSessionId })'), 'sendToOpencode function exists' ); check( serverJs.includes('const clean = sanitizeMessage(content)'), 'sendToOpencode sanitizes content' ); check( serverJs.includes("const args = ['run', '--model', resolvedModel]"), 'sendToOpencode prepares CLI arguments' ); check( serverJs.includes('args.push(clean)'), 'sendToOpencode adds content as argument' ); check( serverJs.includes('const { stdout, stderr } = await runCommand(cliCommand, args'), 'sendToOpencode executes OpenCode CLI' ); console.log('\n--- Streaming Support ---\n'); // Check streaming functionality check( builderJs.includes('function streamMessage(sessionId, messageId)'), 'streamMessage function exists in builder' ); check( builderJs.includes('const url = `/api/sessions/${sessionId}/messages/${messageId}/stream`'), 'streamMessage connects to correct endpoint' ); check( builderJs.includes('const eventSource = new EventSource(url)'), 'streamMessage uses EventSource for SSE' ); check( serverJs.includes('async function handleMessageStream(req, res, sessionId, messageId, userId)'), 'handleMessageStream function exists in server' ); check( serverJs.includes("'Content-Type': 'text/event-stream'"), 'Server sets correct content type for SSE' ); console.log('\n--- Summary ---\n'); const percentage = ((passCount / checkCount) * 100).toFixed(1); console.log(`Checks passed: ${passCount}/${checkCount} (${percentage}%)`); if (allChecks) { console.log('\n✅ ALL CHECKS PASSED'); console.log('\nThe builder message sending flow is correctly implemented.'); console.log('All files are valid and components are properly connected.'); console.log('Messages should successfully flow: Builder → Server → OpenCode\n'); console.log('If messages are not being sent, check:'); console.log(' 1. OpenCode CLI is installed and accessible'); console.log(' 2. Server is running and accessible'); console.log(' 3. User has a valid session'); console.log(' 4. Model is properly configured'); console.log(' 5. Browser console for runtime errors'); console.log(' 6. Server logs for processing errors'); process.exit(0); } else { console.log('\n❌ SOME CHECKS FAILED'); console.log('\nReview the failed checks above to identify issues.\n'); process.exit(1); }