@skillhq/concierge 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +91 -0
  2. package/dist/cli/program.d.ts +3 -0
  3. package/dist/cli/program.d.ts.map +1 -0
  4. package/dist/cli/program.js +46 -0
  5. package/dist/cli/program.js.map +1 -0
  6. package/dist/cli/shared.d.ts +18 -0
  7. package/dist/cli/shared.d.ts.map +1 -0
  8. package/dist/cli/shared.js +2 -0
  9. package/dist/cli/shared.js.map +1 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +5 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands/call.d.ts +7 -0
  15. package/dist/commands/call.d.ts.map +1 -0
  16. package/dist/commands/call.js +409 -0
  17. package/dist/commands/call.js.map +1 -0
  18. package/dist/commands/config.d.ts +4 -0
  19. package/dist/commands/config.d.ts.map +1 -0
  20. package/dist/commands/config.js +120 -0
  21. package/dist/commands/config.js.map +1 -0
  22. package/dist/commands/find-contact.d.ts +4 -0
  23. package/dist/commands/find-contact.d.ts.map +1 -0
  24. package/dist/commands/find-contact.js +57 -0
  25. package/dist/commands/find-contact.js.map +1 -0
  26. package/dist/commands/server.d.ts +7 -0
  27. package/dist/commands/server.d.ts.map +1 -0
  28. package/dist/commands/server.js +212 -0
  29. package/dist/commands/server.js.map +1 -0
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +3 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/lib/call/audio/mulaw.d.ts +35 -0
  35. package/dist/lib/call/audio/mulaw.d.ts.map +1 -0
  36. package/dist/lib/call/audio/mulaw.js +109 -0
  37. package/dist/lib/call/audio/mulaw.js.map +1 -0
  38. package/dist/lib/call/audio/pcm-utils.d.ts +62 -0
  39. package/dist/lib/call/audio/pcm-utils.d.ts.map +1 -0
  40. package/dist/lib/call/audio/pcm-utils.js +149 -0
  41. package/dist/lib/call/audio/pcm-utils.js.map +1 -0
  42. package/dist/lib/call/audio/resample.d.ts +34 -0
  43. package/dist/lib/call/audio/resample.d.ts.map +1 -0
  44. package/dist/lib/call/audio/resample.js +97 -0
  45. package/dist/lib/call/audio/resample.js.map +1 -0
  46. package/dist/lib/call/audio/streaming-decoder.d.ts +45 -0
  47. package/dist/lib/call/audio/streaming-decoder.d.ts.map +1 -0
  48. package/dist/lib/call/audio/streaming-decoder.js +110 -0
  49. package/dist/lib/call/audio/streaming-decoder.js.map +1 -0
  50. package/dist/lib/call/call-server.d.ts +110 -0
  51. package/dist/lib/call/call-server.d.ts.map +1 -0
  52. package/dist/lib/call/call-server.js +681 -0
  53. package/dist/lib/call/call-server.js.map +1 -0
  54. package/dist/lib/call/call-session.d.ts +133 -0
  55. package/dist/lib/call/call-session.d.ts.map +1 -0
  56. package/dist/lib/call/call-session.js +890 -0
  57. package/dist/lib/call/call-session.js.map +1 -0
  58. package/dist/lib/call/call-types.d.ts +133 -0
  59. package/dist/lib/call/call-types.d.ts.map +1 -0
  60. package/dist/lib/call/call-types.js +16 -0
  61. package/dist/lib/call/call-types.js.map +1 -0
  62. package/dist/lib/call/conversation-ai.d.ts +56 -0
  63. package/dist/lib/call/conversation-ai.d.ts.map +1 -0
  64. package/dist/lib/call/conversation-ai.js +276 -0
  65. package/dist/lib/call/conversation-ai.js.map +1 -0
  66. package/dist/lib/call/eval/codec-test.d.ts +45 -0
  67. package/dist/lib/call/eval/codec-test.d.ts.map +1 -0
  68. package/dist/lib/call/eval/codec-test.js +169 -0
  69. package/dist/lib/call/eval/codec-test.js.map +1 -0
  70. package/dist/lib/call/eval/conversation-scripts.d.ts +55 -0
  71. package/dist/lib/call/eval/conversation-scripts.d.ts.map +1 -0
  72. package/dist/lib/call/eval/conversation-scripts.js +359 -0
  73. package/dist/lib/call/eval/conversation-scripts.js.map +1 -0
  74. package/dist/lib/call/eval/eval-runner.d.ts +64 -0
  75. package/dist/lib/call/eval/eval-runner.d.ts.map +1 -0
  76. package/dist/lib/call/eval/eval-runner.js +369 -0
  77. package/dist/lib/call/eval/eval-runner.js.map +1 -0
  78. package/dist/lib/call/eval/index.d.ts +9 -0
  79. package/dist/lib/call/eval/index.d.ts.map +1 -0
  80. package/dist/lib/call/eval/index.js +9 -0
  81. package/dist/lib/call/eval/index.js.map +1 -0
  82. package/dist/lib/call/eval/integration-test-suite.d.ts +71 -0
  83. package/dist/lib/call/eval/integration-test-suite.d.ts.map +1 -0
  84. package/dist/lib/call/eval/integration-test-suite.js +519 -0
  85. package/dist/lib/call/eval/integration-test-suite.js.map +1 -0
  86. package/dist/lib/call/eval/turn-taking-test.d.ts +84 -0
  87. package/dist/lib/call/eval/turn-taking-test.d.ts.map +1 -0
  88. package/dist/lib/call/eval/turn-taking-test.js +260 -0
  89. package/dist/lib/call/eval/turn-taking-test.js.map +1 -0
  90. package/dist/lib/call/index.d.ts +12 -0
  91. package/dist/lib/call/index.d.ts.map +1 -0
  92. package/dist/lib/call/index.js +17 -0
  93. package/dist/lib/call/index.js.map +1 -0
  94. package/dist/lib/call/providers/deepgram.d.ts +81 -0
  95. package/dist/lib/call/providers/deepgram.d.ts.map +1 -0
  96. package/dist/lib/call/providers/deepgram.js +279 -0
  97. package/dist/lib/call/providers/deepgram.js.map +1 -0
  98. package/dist/lib/call/providers/elevenlabs.d.ts +78 -0
  99. package/dist/lib/call/providers/elevenlabs.d.ts.map +1 -0
  100. package/dist/lib/call/providers/elevenlabs.js +272 -0
  101. package/dist/lib/call/providers/elevenlabs.js.map +1 -0
  102. package/dist/lib/call/providers/local-deps.d.ts +18 -0
  103. package/dist/lib/call/providers/local-deps.d.ts.map +1 -0
  104. package/dist/lib/call/providers/local-deps.js +114 -0
  105. package/dist/lib/call/providers/local-deps.js.map +1 -0
  106. package/dist/lib/call/providers/twilio.d.ts +53 -0
  107. package/dist/lib/call/providers/twilio.d.ts.map +1 -0
  108. package/dist/lib/call/providers/twilio.js +173 -0
  109. package/dist/lib/call/providers/twilio.js.map +1 -0
  110. package/dist/lib/concierge-client-types.d.ts +68 -0
  111. package/dist/lib/concierge-client-types.d.ts.map +1 -0
  112. package/dist/lib/concierge-client-types.js +2 -0
  113. package/dist/lib/concierge-client-types.js.map +1 -0
  114. package/dist/lib/concierge-client.d.ts +29 -0
  115. package/dist/lib/concierge-client.d.ts.map +1 -0
  116. package/dist/lib/concierge-client.js +534 -0
  117. package/dist/lib/concierge-client.js.map +1 -0
  118. package/dist/lib/config.d.ts +9 -0
  119. package/dist/lib/config.d.ts.map +1 -0
  120. package/dist/lib/config.js +66 -0
  121. package/dist/lib/config.js.map +1 -0
  122. package/dist/lib/output.d.ts +7 -0
  123. package/dist/lib/output.d.ts.map +1 -0
  124. package/dist/lib/output.js +114 -0
  125. package/dist/lib/output.js.map +1 -0
  126. package/dist/lib/utils/contact-extractor.d.ts +12 -0
  127. package/dist/lib/utils/contact-extractor.d.ts.map +1 -0
  128. package/dist/lib/utils/contact-extractor.js +159 -0
  129. package/dist/lib/utils/contact-extractor.js.map +1 -0
  130. package/dist/lib/utils/formatters.d.ts +15 -0
  131. package/dist/lib/utils/formatters.d.ts.map +1 -0
  132. package/dist/lib/utils/formatters.js +107 -0
  133. package/dist/lib/utils/formatters.js.map +1 -0
  134. package/dist/lib/utils/url-parser.d.ts +11 -0
  135. package/dist/lib/utils/url-parser.d.ts.map +1 -0
  136. package/dist/lib/utils/url-parser.js +103 -0
  137. package/dist/lib/utils/url-parser.js.map +1 -0
  138. package/package.json +67 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * PCM audio buffer utilities
3
+ */
4
+ export interface AudioFormat {
5
+ sampleRate: number;
6
+ channels: number;
7
+ bitDepth: number;
8
+ }
9
+ export declare const TWILIO_FORMAT: AudioFormat;
10
+ export declare const DEEPGRAM_FORMAT: AudioFormat;
11
+ /**
12
+ * Resample 16-bit PCM audio using linear interpolation
13
+ * @param pcm - Input PCM buffer (16-bit little-endian)
14
+ * @param fromRate - Source sample rate
15
+ * @param toRate - Target sample rate
16
+ * @returns Resampled PCM buffer
17
+ */
18
+ export declare function resamplePcm(pcm: Buffer, fromRate: number, toRate: number): Buffer;
19
+ /**
20
+ * Upsample 8kHz to 16kHz using linear interpolation
21
+ */
22
+ export declare function upsample8kTo16k(pcm: Buffer): Buffer;
23
+ /**
24
+ * Downsample 16kHz to 8kHz
25
+ */
26
+ export declare function downsample16kTo8k(pcm: Buffer): Buffer;
27
+ /**
28
+ * Downsample 24kHz to 8kHz
29
+ */
30
+ export declare function downsample24kTo8k(pcm: Buffer): Buffer;
31
+ /**
32
+ * Downsample 44.1kHz to 8kHz
33
+ */
34
+ export declare function downsample44kTo8k(pcm: Buffer): Buffer;
35
+ /**
36
+ * Convert stereo PCM to mono by averaging channels
37
+ */
38
+ export declare function stereoToMono(pcm: Buffer): Buffer;
39
+ /**
40
+ * Normalize audio to a target peak level
41
+ * @param pcm - 16-bit PCM buffer
42
+ * @param targetPeak - Target peak (0-1, default 0.9)
43
+ */
44
+ export declare function normalizePcm(pcm: Buffer, targetPeak?: number): Buffer;
45
+ /**
46
+ * Apply a simple low-pass filter to reduce aliasing before downsampling
47
+ */
48
+ export declare function lowPassFilter(pcm: Buffer, cutoffRatio?: number): Buffer;
49
+ /**
50
+ * Calculate RMS (root mean square) level of audio
51
+ * Returns value from 0-1
52
+ */
53
+ export declare function calculateRms(pcm: Buffer): number;
54
+ /**
55
+ * Check if audio buffer contains silence (below threshold)
56
+ */
57
+ export declare function isSilence(pcm: Buffer, threshold?: number): boolean;
58
+ /**
59
+ * Concatenate multiple PCM buffers
60
+ */
61
+ export declare function concatenatePcm(buffers: Buffer[]): Buffer;
62
+ //# sourceMappingURL=pcm-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pcm-utils.d.ts","sourceRoot":"","sources":["../../../../src/lib/call/audio/pcm-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,aAAa,EAAE,WAI3B,CAAC;AAGF,eAAO,MAAM,eAAe,EAAE,WAI7B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAuBjF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,SAAM,GAAG,MAAM,CAyBlE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,SAAM,GAAG,MAAM,CAepE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUhD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,SAAO,GAAG,OAAO,CAEhE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAExD"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * PCM audio buffer utilities
3
+ */
4
+ // Twilio's audio format: 8kHz mono µ-law
5
+ export const TWILIO_FORMAT = {
6
+ sampleRate: 8000,
7
+ channels: 1,
8
+ bitDepth: 8,
9
+ };
10
+ // Deepgram preferred format: 16kHz mono 16-bit PCM
11
+ export const DEEPGRAM_FORMAT = {
12
+ sampleRate: 16000,
13
+ channels: 1,
14
+ bitDepth: 16,
15
+ };
16
+ /**
17
+ * Resample 16-bit PCM audio using linear interpolation
18
+ * @param pcm - Input PCM buffer (16-bit little-endian)
19
+ * @param fromRate - Source sample rate
20
+ * @param toRate - Target sample rate
21
+ * @returns Resampled PCM buffer
22
+ */
23
+ export function resamplePcm(pcm, fromRate, toRate) {
24
+ if (fromRate === toRate)
25
+ return pcm;
26
+ const ratio = fromRate / toRate;
27
+ const inputSamples = pcm.length / 2;
28
+ const outputSamples = Math.floor(inputSamples / ratio);
29
+ const output = Buffer.alloc(outputSamples * 2);
30
+ for (let i = 0; i < outputSamples; i++) {
31
+ const srcIndex = i * ratio;
32
+ const srcIndexFloor = Math.floor(srcIndex);
33
+ const fraction = srcIndex - srcIndexFloor;
34
+ const sample1 = pcm.readInt16LE(srcIndexFloor * 2);
35
+ const sample2Index = Math.min(srcIndexFloor + 1, inputSamples - 1) * 2;
36
+ const sample2 = pcm.readInt16LE(sample2Index);
37
+ // Linear interpolation
38
+ const interpolated = Math.round(sample1 + (sample2 - sample1) * fraction);
39
+ output.writeInt16LE(interpolated, i * 2);
40
+ }
41
+ return output;
42
+ }
43
+ /**
44
+ * Upsample 8kHz to 16kHz using linear interpolation
45
+ */
46
+ export function upsample8kTo16k(pcm) {
47
+ return resamplePcm(pcm, 8000, 16000);
48
+ }
49
+ /**
50
+ * Downsample 16kHz to 8kHz
51
+ */
52
+ export function downsample16kTo8k(pcm) {
53
+ return resamplePcm(pcm, 16000, 8000);
54
+ }
55
+ /**
56
+ * Downsample 24kHz to 8kHz
57
+ */
58
+ export function downsample24kTo8k(pcm) {
59
+ return resamplePcm(pcm, 24000, 8000);
60
+ }
61
+ /**
62
+ * Downsample 44.1kHz to 8kHz
63
+ */
64
+ export function downsample44kTo8k(pcm) {
65
+ return resamplePcm(pcm, 44100, 8000);
66
+ }
67
+ /**
68
+ * Convert stereo PCM to mono by averaging channels
69
+ */
70
+ export function stereoToMono(pcm) {
71
+ const mono = Buffer.alloc(pcm.length / 2);
72
+ for (let i = 0; i < mono.length / 2; i++) {
73
+ const left = pcm.readInt16LE(i * 4);
74
+ const right = pcm.readInt16LE(i * 4 + 2);
75
+ mono.writeInt16LE(Math.round((left + right) / 2), i * 2);
76
+ }
77
+ return mono;
78
+ }
79
+ /**
80
+ * Normalize audio to a target peak level
81
+ * @param pcm - 16-bit PCM buffer
82
+ * @param targetPeak - Target peak (0-1, default 0.9)
83
+ */
84
+ export function normalizePcm(pcm, targetPeak = 0.9) {
85
+ // Find current peak
86
+ let peak = 0;
87
+ for (let i = 0; i < pcm.length / 2; i++) {
88
+ const sample = Math.abs(pcm.readInt16LE(i * 2));
89
+ if (sample > peak)
90
+ peak = sample;
91
+ }
92
+ if (peak === 0)
93
+ return pcm;
94
+ // Calculate gain
95
+ const targetAmplitude = Math.round(32767 * targetPeak);
96
+ const gain = targetAmplitude / peak;
97
+ // Apply gain
98
+ const output = Buffer.alloc(pcm.length);
99
+ for (let i = 0; i < pcm.length / 2; i++) {
100
+ const sample = pcm.readInt16LE(i * 2);
101
+ const amplified = Math.round(sample * gain);
102
+ // Clamp to 16-bit range
103
+ const clamped = Math.max(-32768, Math.min(32767, amplified));
104
+ output.writeInt16LE(clamped, i * 2);
105
+ }
106
+ return output;
107
+ }
108
+ /**
109
+ * Apply a simple low-pass filter to reduce aliasing before downsampling
110
+ */
111
+ export function lowPassFilter(pcm, cutoffRatio = 0.4) {
112
+ const output = Buffer.alloc(pcm.length);
113
+ const alpha = cutoffRatio;
114
+ let prevSample = pcm.readInt16LE(0);
115
+ output.writeInt16LE(prevSample, 0);
116
+ for (let i = 1; i < pcm.length / 2; i++) {
117
+ const sample = pcm.readInt16LE(i * 2);
118
+ const filtered = Math.round(alpha * sample + (1 - alpha) * prevSample);
119
+ output.writeInt16LE(filtered, i * 2);
120
+ prevSample = filtered;
121
+ }
122
+ return output;
123
+ }
124
+ /**
125
+ * Calculate RMS (root mean square) level of audio
126
+ * Returns value from 0-1
127
+ */
128
+ export function calculateRms(pcm) {
129
+ let sum = 0;
130
+ const samples = pcm.length / 2;
131
+ for (let i = 0; i < samples; i++) {
132
+ const sample = pcm.readInt16LE(i * 2) / 32768;
133
+ sum += sample * sample;
134
+ }
135
+ return Math.sqrt(sum / samples);
136
+ }
137
+ /**
138
+ * Check if audio buffer contains silence (below threshold)
139
+ */
140
+ export function isSilence(pcm, threshold = 0.01) {
141
+ return calculateRms(pcm) < threshold;
142
+ }
143
+ /**
144
+ * Concatenate multiple PCM buffers
145
+ */
146
+ export function concatenatePcm(buffers) {
147
+ return Buffer.concat(buffers);
148
+ }
149
+ //# sourceMappingURL=pcm-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pcm-utils.js","sourceRoot":"","sources":["../../../../src/lib/call/audio/pcm-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Audio resampling utilities for streaming conversion
3
+ * Converts 24kHz PCM to 8kHz µ-law in real-time
4
+ */
5
+ /**
6
+ * Convert a single PCM sample to µ-law
7
+ */
8
+ export declare function pcmSampleToMulaw(sample: number): number;
9
+ /**
10
+ * Streaming resampler: converts 24kHz 16-bit PCM to 8kHz µ-law
11
+ * Uses simple decimation (takes every 3rd sample) - not audiophile quality but fast
12
+ */
13
+ export declare class StreamingResampler {
14
+ private remainder;
15
+ /**
16
+ * Process a chunk of 24kHz PCM and return 8kHz µ-law
17
+ * @param pcm24k - 16-bit little-endian PCM at 24kHz
18
+ * @returns µ-law audio at 8kHz
19
+ */
20
+ process(pcm24k: Buffer): Buffer;
21
+ /**
22
+ * Flush any remaining audio
23
+ */
24
+ flush(): Buffer;
25
+ /**
26
+ * Reset the resampler state
27
+ */
28
+ reset(): void;
29
+ }
30
+ /**
31
+ * Simple one-shot conversion: 24kHz PCM to 8kHz µ-law
32
+ */
33
+ export declare function pcm24kToMulaw8k(pcm24k: Buffer): Buffer;
34
+ //# sourceMappingURL=resample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resample.d.ts","sourceRoot":"","sources":["../../../../src/lib/call/audio/resample.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA8BH;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,SAAS,CAA2B;IAE5C;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IA4B/B;;OAEG;IACH,KAAK,IAAI,MAAM;IAWf;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKtD"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Audio resampling utilities for streaming conversion
3
+ * Converts 24kHz PCM to 8kHz µ-law in real-time
4
+ */
5
+ // µ-law encoding table (linear 16-bit to 8-bit µ-law)
6
+ const LINEAR_TO_MULAW = new Uint8Array(65536);
7
+ // Bias and max for µ-law
8
+ const MULAW_BIAS = 0x84;
9
+ const MULAW_MAX = 0x7fff;
10
+ function linearToMulawSample(sample) {
11
+ const sign = sample < 0 ? 0x00 : 0x80;
12
+ if (sample < 0)
13
+ sample = -sample;
14
+ if (sample > MULAW_MAX)
15
+ sample = MULAW_MAX;
16
+ sample += MULAW_BIAS;
17
+ let exponent = 7;
18
+ for (let mask = 0x4000; !(sample & mask) && exponent > 0; mask >>= 1) {
19
+ exponent--;
20
+ }
21
+ const mantissa = (sample >> (exponent + 3)) & 0x0f;
22
+ return ~(sign | (exponent << 4) | mantissa) & 0xff;
23
+ }
24
+ // Initialize lookup table
25
+ for (let i = 0; i < 65536; i++) {
26
+ const sample = i < 32768 ? i : i - 65536;
27
+ LINEAR_TO_MULAW[i] = linearToMulawSample(sample);
28
+ }
29
+ /**
30
+ * Convert a single PCM sample to µ-law
31
+ */
32
+ export function pcmSampleToMulaw(sample) {
33
+ const index = sample < 0 ? sample + 65536 : sample;
34
+ return LINEAR_TO_MULAW[index];
35
+ }
36
+ /**
37
+ * Streaming resampler: converts 24kHz 16-bit PCM to 8kHz µ-law
38
+ * Uses simple decimation (takes every 3rd sample) - not audiophile quality but fast
39
+ */
40
+ export class StreamingResampler {
41
+ remainder = Buffer.alloc(0);
42
+ /**
43
+ * Process a chunk of 24kHz PCM and return 8kHz µ-law
44
+ * @param pcm24k - 16-bit little-endian PCM at 24kHz
45
+ * @returns µ-law audio at 8kHz
46
+ */
47
+ process(pcm24k) {
48
+ // Combine with any leftover bytes from previous chunk
49
+ const input = Buffer.concat([this.remainder, pcm24k]);
50
+ // Each input sample is 2 bytes (16-bit)
51
+ // We need 3 samples of input (6 bytes) to produce 1 sample of output
52
+ // (24000 / 8000 = 3)
53
+ const inputSamples = Math.floor(input.length / 2);
54
+ const outputSamples = Math.floor(inputSamples / 3);
55
+ const output = Buffer.alloc(outputSamples);
56
+ for (let i = 0; i < outputSamples; i++) {
57
+ // Read every 3rd sample from input
58
+ const inputOffset = i * 3 * 2; // 3 samples * 2 bytes
59
+ const sample = input.readInt16LE(inputOffset);
60
+ // Convert to µ-law
61
+ output[i] = pcmSampleToMulaw(sample);
62
+ }
63
+ // Save any remaining bytes for next chunk
64
+ const consumedBytes = outputSamples * 3 * 2;
65
+ this.remainder = input.slice(consumedBytes);
66
+ return output;
67
+ }
68
+ /**
69
+ * Flush any remaining audio
70
+ */
71
+ flush() {
72
+ if (this.remainder.length >= 2) {
73
+ // Process what we have left
74
+ const sample = this.remainder.readInt16LE(0);
75
+ this.remainder = Buffer.alloc(0);
76
+ return Buffer.from([pcmSampleToMulaw(sample)]);
77
+ }
78
+ this.remainder = Buffer.alloc(0);
79
+ return Buffer.alloc(0);
80
+ }
81
+ /**
82
+ * Reset the resampler state
83
+ */
84
+ reset() {
85
+ this.remainder = Buffer.alloc(0);
86
+ }
87
+ }
88
+ /**
89
+ * Simple one-shot conversion: 24kHz PCM to 8kHz µ-law
90
+ */
91
+ export function pcm24kToMulaw8k(pcm24k) {
92
+ const resampler = new StreamingResampler();
93
+ const main = resampler.process(pcm24k);
94
+ const tail = resampler.flush();
95
+ return Buffer.concat([main, tail]);
96
+ }
97
+ //# sourceMappingURL=resample.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resample.js","sourceRoot":"","sources":["../../../../src/lib/call/audio/resample.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Streaming MP3 to µ-law decoder using ffmpeg
3
+ * Converts MP3 audio chunks to 8kHz µ-law in real-time
4
+ */
5
+ import { EventEmitter } from 'node:events';
6
+ export interface StreamingDecoderEvents {
7
+ data: (mulaw: Buffer) => void;
8
+ error: (error: Error) => void;
9
+ close: () => void;
10
+ }
11
+ /**
12
+ * Streaming MP3 to µ-law decoder
13
+ * Uses ffmpeg subprocess with pipes for real-time conversion
14
+ */
15
+ export declare class StreamingDecoder extends EventEmitter {
16
+ private ffmpeg;
17
+ private isStarted;
18
+ /**
19
+ * Start the decoder
20
+ * Must be called before writing data
21
+ */
22
+ start(): void;
23
+ /**
24
+ * Write MP3 data to the decoder
25
+ * Converted µ-law will be emitted via 'data' events
26
+ */
27
+ write(mp3Data: Buffer): boolean;
28
+ /**
29
+ * Signal end of input and flush remaining data
30
+ */
31
+ end(): void;
32
+ /**
33
+ * Stop the decoder immediately
34
+ */
35
+ stop(): void;
36
+ /**
37
+ * Check if decoder is running
38
+ */
39
+ get running(): boolean;
40
+ }
41
+ /**
42
+ * Create a streaming decoder for TTS audio
43
+ */
44
+ export declare function createStreamingDecoder(): StreamingDecoder;
45
+ //# sourceMappingURL=streaming-decoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-decoder.d.ts","sourceRoot":"","sources":["../../../../src/lib/call/audio/streaming-decoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,SAAS,CAAS;IAE1B;;;OAGG;IACH,KAAK,IAAI,IAAI;IAwDb;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAa/B;;OAEG;IACH,GAAG,IAAI,IAAI;IAMX;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;CACF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Streaming MP3 to µ-law decoder using ffmpeg
3
+ * Converts MP3 audio chunks to 8kHz µ-law in real-time
4
+ */
5
+ import { spawn } from 'node:child_process';
6
+ import { EventEmitter } from 'node:events';
7
+ /**
8
+ * Streaming MP3 to µ-law decoder
9
+ * Uses ffmpeg subprocess with pipes for real-time conversion
10
+ */
11
+ export class StreamingDecoder extends EventEmitter {
12
+ ffmpeg = null;
13
+ isStarted = false;
14
+ /**
15
+ * Start the decoder
16
+ * Must be called before writing data
17
+ */
18
+ start() {
19
+ if (this.isStarted) {
20
+ return;
21
+ }
22
+ // Spawn ffmpeg to convert MP3 (stdin) to µ-law (stdout)
23
+ this.ffmpeg = spawn('ffmpeg', [
24
+ '-hide_banner',
25
+ '-loglevel',
26
+ 'error',
27
+ '-i',
28
+ 'pipe:0', // Read MP3 from stdin
29
+ '-f',
30
+ 'mulaw', // Output format: µ-law
31
+ '-ar',
32
+ '8000', // 8kHz sample rate (Twilio)
33
+ '-ac',
34
+ '1', // Mono
35
+ 'pipe:1', // Write to stdout
36
+ ], {
37
+ stdio: ['pipe', 'pipe', 'pipe'],
38
+ });
39
+ // Handle µ-law output
40
+ this.ffmpeg.stdout?.on('data', (chunk) => {
41
+ this.emit('data', chunk);
42
+ });
43
+ // Handle errors
44
+ this.ffmpeg.stderr?.on('data', (data) => {
45
+ const msg = data.toString().trim();
46
+ if (msg && !msg.includes('size=')) {
47
+ console.error('[StreamingDecoder] ffmpeg:', msg);
48
+ }
49
+ });
50
+ this.ffmpeg.on('error', (err) => {
51
+ this.emit('error', err);
52
+ });
53
+ this.ffmpeg.on('close', (code) => {
54
+ if (code !== 0 && code !== null) {
55
+ console.error(`[StreamingDecoder] ffmpeg exited with code ${code}`);
56
+ }
57
+ this.ffmpeg = null;
58
+ this.isStarted = false;
59
+ this.emit('close');
60
+ });
61
+ this.isStarted = true;
62
+ }
63
+ /**
64
+ * Write MP3 data to the decoder
65
+ * Converted µ-law will be emitted via 'data' events
66
+ */
67
+ write(mp3Data) {
68
+ if (!this.ffmpeg?.stdin?.writable) {
69
+ return false;
70
+ }
71
+ try {
72
+ return this.ffmpeg.stdin.write(mp3Data);
73
+ }
74
+ catch (err) {
75
+ this.emit('error', err instanceof Error ? err : new Error(String(err)));
76
+ return false;
77
+ }
78
+ }
79
+ /**
80
+ * Signal end of input and flush remaining data
81
+ */
82
+ end() {
83
+ if (this.ffmpeg?.stdin) {
84
+ this.ffmpeg.stdin.end();
85
+ }
86
+ }
87
+ /**
88
+ * Stop the decoder immediately
89
+ */
90
+ stop() {
91
+ if (this.ffmpeg) {
92
+ this.ffmpeg.kill('SIGTERM');
93
+ this.ffmpeg = null;
94
+ this.isStarted = false;
95
+ }
96
+ }
97
+ /**
98
+ * Check if decoder is running
99
+ */
100
+ get running() {
101
+ return this.isStarted && this.ffmpeg !== null;
102
+ }
103
+ }
104
+ /**
105
+ * Create a streaming decoder for TTS audio
106
+ */
107
+ export function createStreamingDecoder() {
108
+ return new StreamingDecoder();
109
+ }
110
+ //# sourceMappingURL=streaming-decoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-decoder.js","sourceRoot":"","sources":["../../../../src/lib/call/audio/streaming-decoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Call server - HTTP + WebSocket server for voice calls
3
+ */
4
+ import { EventEmitter } from 'node:events';
5
+ import { CallSession } from './call-session.js';
6
+ import type { CallConfig, CallState } from './call-types.js';
7
+ export interface CallServerOptions {
8
+ port: number;
9
+ publicUrl: string;
10
+ config: CallConfig;
11
+ }
12
+ export interface CallServerEvents {
13
+ started: () => void;
14
+ stopped: () => void;
15
+ call_started: (callId: string) => void;
16
+ call_ended: (callId: string, state: CallState) => void;
17
+ error: (error: Error) => void;
18
+ }
19
+ export declare class CallServer extends EventEmitter {
20
+ private server;
21
+ private controlWss;
22
+ private mediaWss;
23
+ private readonly options;
24
+ private readonly sessions;
25
+ private readonly controlClients;
26
+ private statusReconcileTimer;
27
+ private isPreflightCallId;
28
+ constructor(options: CallServerOptions);
29
+ private timestamp;
30
+ private log;
31
+ private warn;
32
+ private error;
33
+ /**
34
+ * Start the server
35
+ */
36
+ start(): Promise<void>;
37
+ /**
38
+ * Stop the server
39
+ */
40
+ stop(): Promise<void>;
41
+ /**
42
+ * Handle HTTP requests
43
+ */
44
+ private handleHttpRequest;
45
+ /**
46
+ * Health check endpoint
47
+ */
48
+ private handleHealthCheck;
49
+ /**
50
+ * Server status endpoint
51
+ */
52
+ private handleStatusCheck;
53
+ /**
54
+ * Call status endpoint
55
+ */
56
+ private handleCallStatusCheck;
57
+ /**
58
+ * Initiate a new call via HTTP
59
+ */
60
+ private handleCallRequest;
61
+ /**
62
+ * Twilio voice webhook - returns TwiML for Media Streams
63
+ */
64
+ private handleTwilioVoice;
65
+ /**
66
+ * Twilio status callback
67
+ */
68
+ private handleTwilioStatus;
69
+ /**
70
+ * Handle control WebSocket connection
71
+ */
72
+ private handleControlConnection;
73
+ /**
74
+ * Handle control messages from clients
75
+ */
76
+ private handleControlMessage;
77
+ /**
78
+ * Handle media stream WebSocket connection
79
+ * Twilio sends callId in the 'start' event's customParameters, not in the URL
80
+ */
81
+ private handleMediaStreamConnection;
82
+ /**
83
+ * Internal method to initiate a call
84
+ */
85
+ private initiateCallInternal;
86
+ /**
87
+ * Broadcast message to all control clients
88
+ */
89
+ private broadcastToControl;
90
+ /**
91
+ * Get a session by call ID
92
+ */
93
+ getSession(callId: string): CallSession | undefined;
94
+ /**
95
+ * Get all active sessions
96
+ */
97
+ getActiveSessions(): Map<string, CallSession>;
98
+ /**
99
+ * Check if server is running
100
+ */
101
+ get isRunning(): boolean;
102
+ private startStatusReconcileLoop;
103
+ private reconcileStatusesWithProvider;
104
+ private preflightPublicWebhook;
105
+ }
106
+ /**
107
+ * Create and configure a call server
108
+ */
109
+ export declare function createCallServer(config: CallConfig, port: number, publicUrl: string): CallServer;
110
+ //# sourceMappingURL=call-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-server.d.ts","sourceRoot":"","sources":["../../../src/lib/call/call-server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAA4C,MAAM,iBAAiB,CAAC;AAyBvG,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACvD,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,oBAAoB,CAA+B;IAE3D,OAAO,CAAC,iBAAiB;gBAIb,OAAO,EAAE,iBAAiB;IAKtC,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,KAAK;IAQb;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmD5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAmCzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAW7B;;OAEG;YACW,iBAAiB;IA2D/B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuDzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAgF1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA6B/B;;OAEG;YACW,oBAAoB;IAmClC;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAyDnC;;OAEG;YACW,oBAAoB;IA+DlC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAInD;;OAEG;IACH,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC;IAI7C;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,CAAC,wBAAwB;YAWlB,6BAA6B;YAiC7B,sBAAsB;CAoErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAMhG"}