fix(ts-config,proxybridge,voicebox): align voicebox config types and add missing proxy bridge command definitions
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: 'siprouter',
|
||||
version: '1.20.2',
|
||||
version: '1.20.3',
|
||||
description: 'undefined'
|
||||
}
|
||||
|
||||
26
ts/config.ts
26
ts/config.ts
@@ -8,6 +8,7 @@
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import type { IVoiceboxConfig } from './voicebox.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared types (previously in ts/sip/types.ts, now inlined)
|
||||
@@ -160,24 +161,13 @@ export interface IContact {
|
||||
// Voicebox configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface IVoiceboxConfig {
|
||||
/** Unique ID — typically matches device ID or extension. */
|
||||
id: string;
|
||||
/** Whether this voicebox is active. */
|
||||
enabled: boolean;
|
||||
/** Custom TTS greeting text. */
|
||||
greetingText?: string;
|
||||
/** TTS voice ID (default 'af_bella'). */
|
||||
greetingVoice?: string;
|
||||
/** Path to uploaded WAV greeting (overrides TTS). */
|
||||
greetingWavPath?: string;
|
||||
/** Seconds to wait before routing to voicemail (default 25). */
|
||||
noAnswerTimeoutSec?: number;
|
||||
/** Maximum recording duration in seconds (default 120). */
|
||||
maxRecordingSec?: number;
|
||||
/** Maximum stored messages per box (default 50). */
|
||||
maxMessages?: number;
|
||||
}
|
||||
// Canonical definition lives in voicebox.ts (imported at the top of this
|
||||
// file) — re-exported here so consumers can import everything from a
|
||||
// single config module without pulling in the voicebox implementation.
|
||||
// This used to be a duplicated interface and caused
|
||||
// "number | undefined is not assignable to number" type errors when
|
||||
// passing config.voiceboxes into VoiceboxManager.init().
|
||||
export type { IVoiceboxConfig };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IVR configuration
|
||||
|
||||
@@ -41,6 +41,14 @@ type TProxyCommands = {
|
||||
params: { call_id: string };
|
||||
result: { file_path: string; duration_ms: number };
|
||||
};
|
||||
add_leg: {
|
||||
params: { call_id: string; number: string; provider_id?: string };
|
||||
result: { leg_id: string };
|
||||
};
|
||||
remove_leg: {
|
||||
params: { call_id: string; leg_id: string };
|
||||
result: Record<string, never>;
|
||||
};
|
||||
add_device_leg: {
|
||||
params: { call_id: string; device_id: string };
|
||||
result: { leg_id: string };
|
||||
@@ -83,6 +91,34 @@ type TProxyCommands = {
|
||||
params: { model: string; voices: string; voice: string; text: string; output: string };
|
||||
result: { output: string };
|
||||
};
|
||||
// WebRTC signaling — bridged from the browser via the TS control plane.
|
||||
webrtc_offer: {
|
||||
params: { session_id: string; sdp: string };
|
||||
result: { sdp: string };
|
||||
};
|
||||
webrtc_ice: {
|
||||
params: {
|
||||
session_id: string;
|
||||
candidate: string;
|
||||
sdp_mid?: string;
|
||||
sdp_mline_index?: number;
|
||||
};
|
||||
result: Record<string, never>;
|
||||
};
|
||||
webrtc_link: {
|
||||
params: {
|
||||
session_id: string;
|
||||
call_id: string;
|
||||
provider_media_addr: string;
|
||||
provider_media_port: number;
|
||||
sip_pt?: number;
|
||||
};
|
||||
result: Record<string, never>;
|
||||
};
|
||||
webrtc_close: {
|
||||
params: { session_id: string };
|
||||
result: Record<string, never>;
|
||||
};
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -522,7 +558,7 @@ export async function sendProxyCommand<K extends keyof TProxyCommands>(
|
||||
params: TProxyCommands[K]['params'],
|
||||
): Promise<TProxyCommands[K]['result']> {
|
||||
if (!bridge || !initialized) throw new Error('proxy engine not initialized');
|
||||
return bridge.sendCommand(method as string, params as any) as any;
|
||||
return bridge.sendCommand(method, params) as Promise<TProxyCommands[K]['result']>;
|
||||
}
|
||||
|
||||
/** Shut down the proxy engine. */
|
||||
|
||||
@@ -501,7 +501,7 @@ async function startProxyEngine(): Promise<void> {
|
||||
onProxyEvent('recording_done', (data: any) => {
|
||||
log(`[voicemail] recording done: ${data.file_path} (${data.duration_ms}ms) caller=${data.caller_number}`);
|
||||
// Save voicemail metadata via VoiceboxManager.
|
||||
voiceboxManager.addMessage?.('default', {
|
||||
voiceboxManager.addMessage('default', {
|
||||
callerNumber: data.caller_number || 'Unknown',
|
||||
callerName: null,
|
||||
fileName: data.file_path,
|
||||
|
||||
@@ -29,12 +29,14 @@ export interface IVoiceboxConfig {
|
||||
greetingVoice?: string;
|
||||
/** Path to uploaded WAV greeting (overrides TTS). */
|
||||
greetingWavPath?: string;
|
||||
/** Seconds to wait before routing to voicemail (default 25). */
|
||||
noAnswerTimeoutSec: number;
|
||||
/** Maximum recording duration in seconds (default 120). */
|
||||
maxRecordingSec: number;
|
||||
/** Maximum stored messages per box (default 50). */
|
||||
maxMessages: number;
|
||||
/** Seconds to wait before routing to voicemail. Defaults to 25 when
|
||||
* absent — both the config loader and `VoiceboxManager.init` apply
|
||||
* the default via `??=`. */
|
||||
noAnswerTimeoutSec?: number;
|
||||
/** Maximum recording duration in seconds. Defaults to 120. */
|
||||
maxRecordingSec?: number;
|
||||
/** Maximum stored messages per box. Defaults to 50. */
|
||||
maxMessages?: number;
|
||||
}
|
||||
|
||||
export interface IVoicemailMessage {
|
||||
@@ -148,6 +150,35 @@ export class VoiceboxManager {
|
||||
// Message CRUD
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Convenience wrapper around `saveMessage` — used by the `recording_done`
|
||||
* event handler, which has a raw recording path + caller info and needs
|
||||
* to persist metadata. Generates `id`, sets `timestamp = now`, defaults
|
||||
* `heard = false`, and normalizes `fileName` to a basename (the WAV is
|
||||
* expected to already live in the box's directory).
|
||||
*/
|
||||
addMessage(
|
||||
boxId: string,
|
||||
info: {
|
||||
callerNumber: string;
|
||||
callerName?: string | null;
|
||||
fileName: string;
|
||||
durationMs: number;
|
||||
},
|
||||
): void {
|
||||
const msg: IVoicemailMessage = {
|
||||
id: crypto.randomUUID(),
|
||||
boxId,
|
||||
callerNumber: info.callerNumber,
|
||||
callerName: info.callerName ?? undefined,
|
||||
timestamp: Date.now(),
|
||||
durationMs: info.durationMs,
|
||||
fileName: path.basename(info.fileName),
|
||||
heard: false,
|
||||
};
|
||||
this.saveMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new voicemail message.
|
||||
* The WAV file should already exist at the expected path.
|
||||
|
||||
Reference in New Issue
Block a user