feat(proxy-engine): add Rust-based outbound calling, WebRTC bridging, and voicemail handling

This commit is contained in:
2026-04-10 11:36:18 +00:00
parent ad253f823f
commit 239e2ac81d
42 changed files with 3360 additions and 6444 deletions

View File

@@ -11,10 +11,13 @@ import path from 'node:path';
import http from 'node:http';
import https from 'node:https';
import { WebSocketServer, WebSocket } from 'ws';
import type { CallManager } from './call/index.ts';
import { handleWebRtcSignaling } from './webrtcbridge.ts';
import type { VoiceboxManager } from './voicebox.ts';
// CallManager was previously used for WebRTC call handling. Now replaced by Rust proxy-engine.
// Kept as `any` type for backward compat with the function signature until full WebRTC port.
type CallManager = any;
const CONFIG_PATH = path.join(process.cwd(), '.nogit', 'config.json');
// ---------------------------------------------------------------------------
@@ -336,6 +339,10 @@ export function initWebUi(
onHangupCall: (callId: string) => boolean,
onConfigSaved?: () => void,
callManager?: CallManager,
/** WebRTC signaling handlers — forwarded to Rust proxy-engine. */
onWebRtcOffer?: (sessionId: string, sdp: string, ws: WebSocket) => Promise<void>,
onWebRtcIce?: (sessionId: string, candidate: any) => Promise<void>,
onWebRtcClose?: (sessionId: string) => Promise<void>,
voiceboxManager?: VoiceboxManager,
): void {
const WEB_PORT = 3060;
@@ -372,17 +379,23 @@ export function initWebUi(
socket.on('message', (raw) => {
try {
const msg = JSON.parse(raw.toString());
if (msg.type === 'webrtc-accept' && msg.callId) {
log(`[webrtc] browser accepted call ${msg.callId} session=${msg.sessionId || 'none'}`);
const ok = callManager?.acceptBrowserCall(msg.callId, msg.sessionId) ?? false;
log(`[webrtc] acceptBrowserCall result: ${ok}`);
} else if (msg.type === 'webrtc-offer' && msg.sessionId) {
callManager?.handleWebRtcOffer(msg.sessionId, msg.sdp, socket as any).catch((e: any) =>
log(`[webrtc] offer error: ${e.message}`));
if (msg.type === 'webrtc-offer' && msg.sessionId) {
// Forward to Rust proxy-engine for WebRTC handling.
if (onWebRtcOffer) {
onWebRtcOffer(msg.sessionId, msg.sdp, socket as any).catch((e: any) =>
log(`[webrtc] offer error: ${e.message}`));
}
} else if (msg.type === 'webrtc-ice' && msg.sessionId) {
callManager?.handleWebRtcIce(msg.sessionId, msg.candidate).catch(() => {});
if (onWebRtcIce) {
onWebRtcIce(msg.sessionId, msg.candidate).catch(() => {});
}
} else if (msg.type === 'webrtc-hangup' && msg.sessionId) {
callManager?.handleWebRtcHangup(msg.sessionId);
if (onWebRtcClose) {
onWebRtcClose(msg.sessionId).catch(() => {});
}
} else if (msg.type === 'webrtc-accept' && msg.callId) {
// TODO: Wire to Rust call linking.
log(`[webrtc] accept: call=${msg.callId} session=${msg.sessionId || 'none'}`);
} else if (msg.type?.startsWith('webrtc-')) {
msg._remoteIp = remoteIp;
handleWebRtcSignaling(socket as any, msg);