✅ What's Working: - WebSocket connections established (patched worker to intercept upgrades) - Real-time event streaming: Agent → DO → Browser - Terminal panel showing live command execution - Agent chat panel showing LLM thoughts - Full infrastructure: UI → API → DO → SSH Proxy → LangGraph Agent 🔧 Key Changes: - Created standalone DO worker at workers/bandit-agent-do/ - Deployed DO as separate Worker (bandit-agent-do) - Updated wrangler.jsonc to reference external DO via script_name - Modified patch-worker.js to intercept WS upgrades before Next.js - Added __name polyfill to fix esbuild helper - Created pnpm workspace config for monorepo 📝 Architecture: - Frontend (Next.js) → Cloudflare Worker - Worker intercepts /api/agent/*/ws → forwards to DO - DO (bandit-agent-do) → manages WebSocket connections - DO → calls SSH Proxy API - SSH Proxy → runs LangGraph agent → executes SSH commands - Events stream back: SSH Proxy → DO → WebSocket → UI 🐛 Known Issue: - Agent logic needs refinement (not parsing SSH output correctly) - But core infrastructure is 100% functional! This resolves all WebSocket and real-time streaming issues.
93 lines
2.9 KiB
JavaScript
93 lines
2.9 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Patch the OpenNext worker to add WebSocket handling
|
|
* Intercepts WebSocket requests before they reach Next.js
|
|
*/
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
console.log('🔨 Patching worker to add WebSocket handler...')
|
|
|
|
const workerPath = path.join(__dirname, '../.open-next/worker.js')
|
|
|
|
if (!fs.existsSync(workerPath)) {
|
|
console.error('❌ Worker file not found at:', workerPath)
|
|
process.exit(1)
|
|
}
|
|
|
|
// Read worker file
|
|
let workerContent = fs.readFileSync(workerPath, 'utf-8')
|
|
|
|
// Check if already patched
|
|
if (workerContent.includes('// WebSocket Intercept Handler')) {
|
|
console.log('✅ Worker already patched, skipping')
|
|
process.exit(0)
|
|
}
|
|
|
|
// Create WebSocket intercept handler
|
|
const wsInterceptCode = `
|
|
// WebSocket Intercept Handler
|
|
function handleWebSocketUpgrade(request, env) {
|
|
const url = new URL(request.url);
|
|
const upgradeHeader = request.headers.get('Upgrade');
|
|
|
|
// Check if this is a WebSocket upgrade for agent endpoints
|
|
if (upgradeHeader === 'websocket' && url.pathname.includes('/api/agent/') && url.pathname.endsWith('/ws')) {
|
|
// Extract runId from path: /api/agent/{runId}/ws
|
|
const pathParts = url.pathname.split('/');
|
|
const runIdIndex = pathParts.indexOf('agent') + 1;
|
|
const runId = pathParts[runIdIndex];
|
|
|
|
if (runId && env.BANDIT_AGENT) {
|
|
// Forward directly to Durable Object
|
|
const id = env.BANDIT_AGENT.idFromName(runId);
|
|
const stub = env.BANDIT_AGENT.get(id);
|
|
return stub.fetch(request);
|
|
}
|
|
}
|
|
|
|
return null; // Not a WebSocket request, continue normal handling
|
|
}
|
|
`;
|
|
|
|
// Find where to inject the WebSocket intercept
|
|
const fetchFunctionStart = workerContent.indexOf('export default {');
|
|
if (fetchFunctionStart === -1) {
|
|
console.error('❌ Could not find export default in worker.js');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Find the async fetch function
|
|
const asyncFetchStart = workerContent.indexOf('async fetch(request, env, ctx) {', fetchFunctionStart);
|
|
if (asyncFetchStart === -1) {
|
|
console.error('❌ Could not find async fetch function in worker.js');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Find the opening brace of the fetch function
|
|
const fetchBodyStart = workerContent.indexOf('{', asyncFetchStart) + 1;
|
|
|
|
// Find the first return statement in the fetch body
|
|
const returnStatement = workerContent.indexOf('return', fetchBodyStart);
|
|
|
|
// Insert WebSocket intercept at the beginning of fetch, before the return
|
|
const patchedContent =
|
|
workerContent.slice(0, fetchBodyStart) +
|
|
wsInterceptCode +
|
|
`
|
|
// Check for WebSocket upgrades first (before Next.js)
|
|
const wsResponse = handleWebSocketUpgrade(request, env);
|
|
if (wsResponse) {
|
|
return wsResponse;
|
|
}
|
|
|
|
` +
|
|
workerContent.slice(fetchBodyStart);
|
|
|
|
// Write back
|
|
fs.writeFileSync(workerPath, patchedContent, 'utf-8');
|
|
|
|
console.log('✅ Worker patched successfully - WebSocket handler added');
|
|
console.log('📝 Note: WebSocket requests now bypass Next.js and go directly to DO');
|