feat(cdp): Phase 2 - real-time UI mirroring via screencast + remote input

This commit is contained in:
2026-03-07 20:41:53 +09:00
parent 9234be33db
commit 4b855c9e57
6 changed files with 526 additions and 1 deletions

View File

@@ -43,6 +43,9 @@ wss.on('connection', (ws) => {
const state = wsClients.get(ws);
if (state?.activeSessionId) {
stopSessionPolling(state.activeSessionId);
// 스크린캠스트도 중지
const session = sessionManager.getSession(state.activeSessionId);
if (session) session.client.stopScreencast();
}
wsClients.delete(ws);
console.log('[WS] 클라이언트 연결 해제');
@@ -126,6 +129,56 @@ async function handleWsMessage(ws, msg) {
break;
}
// ─── Phase 2: Screencast & Remote Input ────────────
case 'start_screencast': {
const session = sessionManager.getSession(msg.sessionId || state.activeSessionId);
if (!session) {
ws.send(JSON.stringify({ type: 'error', message: '활성 세션 없음' }));
return;
}
// 스크린캠스트 프레임 콜백 등록
session.client.onScreencastFrame = (frame) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
type: 'screencast_frame',
sessionId: session.id,
data: frame.data,
metadata: frame.metadata,
}));
}
};
const ok = await session.client.startScreencast(msg.options || {});
ws.send(JSON.stringify({
type: 'screencast_started',
sessionId: session.id,
success: ok,
}));
break;
}
case 'stop_screencast': {
const session = sessionManager.getSession(msg.sessionId || state.activeSessionId);
if (session) {
await session.client.stopScreencast();
}
ws.send(JSON.stringify({
type: 'screencast_stopped',
sessionId: msg.sessionId || state.activeSessionId,
}));
break;
}
case 'input_event': {
const session = sessionManager.getSession(msg.sessionId || state.activeSessionId);
if (session) {
await session.client.dispatchInput(msg.event);
}
break;
}
default:
ws.send(JSON.stringify({ type: 'error', message: `알 수 없는 메시지 타입: ${msg.type}` }));
}