Agent-JAE/packages/web-ui/example/server/browser-server.mjs
JAE 97cef8b4d3
Some checks are pending
CI / build-check-test (push) Waiting to run
feat: add embedded terminal (xterm.js) and playwright browser panel
- Fix empty state overlay covering chat input (bottom:130px offset)
- Fix suggestion chips click-through with corrected z-layering
- Fix handleSuggestion to use chatPanel.agentInterface.setInput()
- Add JaeTerminalPanel: xterm.js + WebSocket bash shell (port 7701)
- Add JaeBrowserPanel: Playwright chromium screenshots (port 7702)
- Add terminal/browser toggle buttons in header toolbar
- Add collapsible right panel with Terminal/Browser tabs
- Add server/terminal-server.mjs and server/browser-server.mjs
- Add npm run dev:all script (Vite + terminal + browser servers)
2026-03-26 20:10:57 +00:00

65 lines
2.4 KiB
JavaScript

import { WebSocketServer } from 'ws';
import { chromium } from 'playwright';
const PORT = 7702;
const wss = new WebSocketServer({ port: PORT });
console.log(`Browser WS server on ws://localhost:${PORT}`);
let browser = null;
async function getBrowser() {
if (!browser) browser = await chromium.launch({ headless: true, args: ['--no-sandbox','--disable-setuid-sandbox'] });
return browser;
}
wss.on('connection', async (ws) => {
let context = null;
let page = null;
async function screenshot() {
if (!page) return;
try {
const buf = await page.screenshot({ type: 'jpeg', quality: 70, fullPage: false });
ws.send(JSON.stringify({ type: 'screenshot', data: buf.toString('base64'), url: page.url() }));
} catch(e) { ws.send(JSON.stringify({ type: 'error', msg: String(e) })); }
}
async function navigate(url) {
try {
if (!context) {
const b = await getBrowser();
context = await b.newContext({ viewport: { width: 1280, height: 800 } });
page = await context.newPage();
}
if (!url.startsWith('http')) url = 'https://' + url;
ws.send(JSON.stringify({ type: 'loading' }));
await page.goto(url, { timeout: 30000, waitUntil: 'domcontentloaded' });
await screenshot();
} catch(e) { ws.send(JSON.stringify({ type: 'error', msg: String(e) })); }
}
ws.on('message', async (msg) => {
try {
const m = JSON.parse(msg.toString());
if (m.type === 'navigate') await navigate(m.url);
if (m.type === 'screenshot') await screenshot();
if (m.type === 'click') {
if (page) { await page.mouse.click(m.x, m.y); await screenshot(); }
}
if (m.type === 'scroll') {
if (page) { await page.mouse.wheel(0, m.dy); await screenshot(); }
}
if (m.type === 'type') {
if (page) { await page.keyboard.type(m.text); await screenshot(); }
}
if (m.type === 'back') { if (page) { await page.goBack(); await screenshot(); } }
if (m.type === 'fwd') { if (page) { await page.goForward(); await screenshot(); } }
if (m.type === 'reload'){ if (page) { await page.reload(); await screenshot(); } }
} catch(e) { ws.send(JSON.stringify({ type: 'error', msg: String(e) })); }
});
ws.on('close', async () => { if (context) await context.close().catch(()=>{}); context = null; page = null; });
// Send welcome screenshot placeholder
ws.send(JSON.stringify({ type: 'ready' }));
});