@jambonz/mrf 0.1.7 → 0.1.9

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.
package/lib/endpoint.js CHANGED
@@ -130,9 +130,10 @@ class Endpoint extends EventEmitter {
130
130
  return { body: `-ERR ${err.message}` };
131
131
  }
132
132
  }
133
- /* uuid_openai_s2s / uuid_voice_agent_s2s: the llm task interface.
134
- * args arrive '^^|<uuid>|<command>[|...]'; map to s2s.* commands. */
135
- const s2 = /^uuid_(openai|voice_agent)_s2s$/.exec(command);
133
+ /* uuid_<vendor>_s2s: the llm task interface. args arrive
134
+ * '^^|<uuid>|<command>[|...]'; map to s2s.* commands. ultravox passes
135
+ * only host/path (a pre-authenticated joinUrl — no authType/apiKey). */
136
+ const s2 = /^uuid_(openai|voice_agent|ultravox)_s2s$/.exec(command);
136
137
  if (s2) {
137
138
  const vendor = s2[1];
138
139
  const raw = Array.isArray(args) ? args.join('|') : String(args || '');
@@ -432,11 +433,45 @@ class Endpoint extends EventEmitter {
432
433
  });
433
434
  return this;
434
435
  }
435
- dub() {
436
- return Promise.reject(new Error('mediajam: dub pending Phase 2 (dub.*)'));
436
+ /* dub (mod_dub): fsmrf passes {action, track, play, loop, gain, say}.
437
+ * Map each action onto the mediajam dub.* command family. gain is in
438
+ * dB (the dub verb's gain), carried as gainDb. */
439
+ async dub(opts = {}) {
440
+ const {action, track} = opts;
441
+ switch (action) {
442
+ case 'addTrack':
443
+ await this._request('dub.addTrack', {track});
444
+ break;
445
+ case 'removeTrack':
446
+ await this._request('dub.removeTrack', {track});
447
+ break;
448
+ case 'silenceTrack':
449
+ await this._request('dub.silenceTrack', {track});
450
+ break;
451
+ case 'playOnTrack':
452
+ await this._request('dub.playOnTrack', {
453
+ track,
454
+ url: opts.play,
455
+ ...(opts.loop && {loop: true}),
456
+ ...(typeof opts.gain === 'number' && opts.gain !== 0 && {gainDb: opts.gain})
457
+ });
458
+ break;
459
+ case 'sayOnTrack':
460
+ await this._request('dub.sayOnTrack', {
461
+ track,
462
+ say: opts.say,
463
+ ...(typeof opts.gain === 'number' && opts.gain !== 0 && {gainDb: opts.gain})
464
+ });
465
+ break;
466
+ default:
467
+ throw new Error(`mediajam: unknown dub action '${action}'`);
468
+ }
469
+ return this;
437
470
  }
438
- setGain() {
439
- return Promise.reject(new Error('mediajam: setGain pending Phase 2 (dub.*)'));
471
+ /* mod_dub setGain — scales the channel audio; maps to endpoint.set gainDb */
472
+ async setGain(gain) {
473
+ await this._request('endpoint.set', {gainDb: typeof gain === 'number' ? gain : parseInt(gain, 10) || 0});
474
+ return this;
440
475
  }
441
476
  join() {
442
477
  return Promise.reject(new Error('mediajam: conference join pending Phase 3 (room.*)'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/mrf",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "node --test",
@@ -92,6 +92,36 @@ test('execute send_dtmf maps digits and duration', async(t) => {
92
92
  assert.equal(req.data.durationMs, 150);
93
93
  });
94
94
 
95
+ test('dub actions map to dub.* commands', async(t) => {
96
+ const { ms, mock } = await setup(t);
97
+ const ep = await ms.createEndpoint({});
98
+ await ep.dub({ action: 'addTrack', track: 'music' });
99
+ await ep.dub({ action: 'playOnTrack', track: 'music', play: 'http://x/a.mp3', loop: true, gain: 5 });
100
+ await ep.dub({ action: 'sayOnTrack', track: 'music', say: 'say:{vendor=google}hello', gain: -3 });
101
+ await ep.dub({ action: 'silenceTrack', track: 'music' });
102
+ await ep.dub({ action: 'removeTrack', track: 'music' });
103
+
104
+ const add = mock.requests.find((r) => r.cmd === 'dub.addTrack');
105
+ assert.equal(add.data.track, 'music');
106
+ const play = mock.requests.find((r) => r.cmd === 'dub.playOnTrack');
107
+ assert.equal(play.data.url, 'http://x/a.mp3');
108
+ assert.equal(play.data.loop, true);
109
+ assert.equal(play.data.gainDb, 5);
110
+ const say = mock.requests.find((r) => r.cmd === 'dub.sayOnTrack');
111
+ assert.equal(say.data.say, 'say:{vendor=google}hello');
112
+ assert.equal(say.data.gainDb, -3);
113
+ assert.ok(mock.requests.find((r) => r.cmd === 'dub.silenceTrack'));
114
+ assert.ok(mock.requests.find((r) => r.cmd === 'dub.removeTrack'));
115
+ });
116
+
117
+ test('setGain maps to endpoint.set gainDb', async(t) => {
118
+ const { ms, mock } = await setup(t);
119
+ const ep = await ms.createEndpoint({});
120
+ await ep.setGain(7);
121
+ const req = mock.requests.filter((r) => r.cmd === 'endpoint.set').pop();
122
+ assert.equal(req.data.gainDb, 7);
123
+ });
124
+
95
125
  test('bridge and unbridge', async(t) => {
96
126
  const { ms, mock } = await setup(t);
97
127
  const a = await ms.createEndpoint({});
@@ -119,6 +119,11 @@ class MockMediajam {
119
119
  case 'endpoint.unmute':
120
120
  case 'bridge.create':
121
121
  case 'bridge.destroy':
122
+ case 'dub.addTrack':
123
+ case 'dub.removeTrack':
124
+ case 'dub.silenceTrack':
125
+ case 'dub.playOnTrack':
126
+ case 'dub.sayOnTrack':
122
127
  res({});
123
128
  break;
124
129
  case 'endpoint.info':