@plumbus/voice-livekit 0.1.2

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 (101) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +146 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/client/client-noise-cancellation.d.ts +15 -0
  5. package/dist/client/client-noise-cancellation.d.ts.map +1 -0
  6. package/dist/client/client-noise-cancellation.js +54 -0
  7. package/dist/client/client-noise-cancellation.js.map +1 -0
  8. package/dist/client/livekit-session.d.ts +21 -0
  9. package/dist/client/livekit-session.d.ts.map +1 -0
  10. package/dist/client/livekit-session.js +206 -0
  11. package/dist/client/livekit-session.js.map +1 -0
  12. package/dist/client/pcm-resample.d.ts +7 -0
  13. package/dist/client/pcm-resample.d.ts.map +1 -0
  14. package/dist/client/pcm-resample.js +87 -0
  15. package/dist/client/pcm-resample.js.map +1 -0
  16. package/dist/client/session-helpers.d.ts +19 -0
  17. package/dist/client/session-helpers.d.ts.map +1 -0
  18. package/dist/client/session-helpers.js +70 -0
  19. package/dist/client/session-helpers.js.map +1 -0
  20. package/dist/client.d.ts +5 -0
  21. package/dist/client.d.ts.map +1 -0
  22. package/dist/client.js +4 -0
  23. package/dist/client.js.map +1 -0
  24. package/dist/cost/record-livekit-transport.d.ts +9 -0
  25. package/dist/cost/record-livekit-transport.d.ts.map +1 -0
  26. package/dist/cost/record-livekit-transport.js +29 -0
  27. package/dist/cost/record-livekit-transport.js.map +1 -0
  28. package/dist/credentials.d.ts +3 -0
  29. package/dist/credentials.d.ts.map +1 -0
  30. package/dist/credentials.js +8 -0
  31. package/dist/credentials.js.map +1 -0
  32. package/dist/descriptor.d.ts +3 -0
  33. package/dist/descriptor.d.ts.map +1 -0
  34. package/dist/descriptor.js +14 -0
  35. package/dist/descriptor.js.map +1 -0
  36. package/dist/index.d.ts +15 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +12 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/noise-cancellation/create-inbound-audio-stream.d.ts +9 -0
  41. package/dist/noise-cancellation/create-inbound-audio-stream.d.ts.map +1 -0
  42. package/dist/noise-cancellation/create-inbound-audio-stream.js +13 -0
  43. package/dist/noise-cancellation/create-inbound-audio-stream.js.map +1 -0
  44. package/dist/noise-cancellation/oss/client-rnnoise-processor.d.ts +2 -0
  45. package/dist/noise-cancellation/oss/client-rnnoise-processor.d.ts.map +1 -0
  46. package/dist/noise-cancellation/oss/client-rnnoise-processor.js +2 -0
  47. package/dist/noise-cancellation/oss/client-rnnoise-processor.js.map +1 -0
  48. package/dist/noise-cancellation/oss/dtln-frame-processor.d.ts +10 -0
  49. package/dist/noise-cancellation/oss/dtln-frame-processor.d.ts.map +1 -0
  50. package/dist/noise-cancellation/oss/dtln-frame-processor.js +118 -0
  51. package/dist/noise-cancellation/oss/dtln-frame-processor.js.map +1 -0
  52. package/dist/noise-cancellation/oss/rnnoise-frame-processor.d.ts +9 -0
  53. package/dist/noise-cancellation/oss/rnnoise-frame-processor.d.ts.map +1 -0
  54. package/dist/noise-cancellation/oss/rnnoise-frame-processor.js +149 -0
  55. package/dist/noise-cancellation/oss/rnnoise-frame-processor.js.map +1 -0
  56. package/dist/noise-cancellation/resolve-noise-cancellation.d.ts +5 -0
  57. package/dist/noise-cancellation/resolve-noise-cancellation.d.ts.map +1 -0
  58. package/dist/noise-cancellation/resolve-noise-cancellation.js +38 -0
  59. package/dist/noise-cancellation/resolve-noise-cancellation.js.map +1 -0
  60. package/dist/pricing.d.ts +8 -0
  61. package/dist/pricing.d.ts.map +1 -0
  62. package/dist/pricing.js +19 -0
  63. package/dist/pricing.js.map +1 -0
  64. package/dist/runtime/consume-audio-stream.d.ts +3 -0
  65. package/dist/runtime/consume-audio-stream.d.ts.map +1 -0
  66. package/dist/runtime/consume-audio-stream.js +15 -0
  67. package/dist/runtime/consume-audio-stream.js.map +1 -0
  68. package/dist/runtime/livekit-agent-worker.d.ts +32 -0
  69. package/dist/runtime/livekit-agent-worker.d.ts.map +1 -0
  70. package/dist/runtime/livekit-agent-worker.js +491 -0
  71. package/dist/runtime/livekit-agent-worker.js.map +1 -0
  72. package/dist/runtime/optional-cloud-registrations.d.ts +12 -0
  73. package/dist/runtime/optional-cloud-registrations.d.ts.map +1 -0
  74. package/dist/runtime/optional-cloud-registrations.js +67 -0
  75. package/dist/runtime/optional-cloud-registrations.js.map +1 -0
  76. package/dist/runtime/parse-participant-context.d.ts +16 -0
  77. package/dist/runtime/parse-participant-context.d.ts.map +1 -0
  78. package/dist/runtime/parse-participant-context.js +61 -0
  79. package/dist/runtime/parse-participant-context.js.map +1 -0
  80. package/dist/runtime/voice-room-worker.d.ts +55 -0
  81. package/dist/runtime/voice-room-worker.d.ts.map +1 -0
  82. package/dist/runtime/voice-room-worker.js +151 -0
  83. package/dist/runtime/voice-room-worker.js.map +1 -0
  84. package/dist/transport/livekit-transport.d.ts +37 -0
  85. package/dist/transport/livekit-transport.d.ts.map +1 -0
  86. package/dist/transport/livekit-transport.js +324 -0
  87. package/dist/transport/livekit-transport.js.map +1 -0
  88. package/dist/types.d.ts +77 -0
  89. package/dist/types.d.ts.map +1 -0
  90. package/dist/types.js +2 -0
  91. package/dist/types.js.map +1 -0
  92. package/dist/worker.d.ts +4 -0
  93. package/dist/worker.d.ts.map +1 -0
  94. package/dist/worker.js +3 -0
  95. package/dist/worker.js.map +1 -0
  96. package/instructions/README.md +27 -0
  97. package/instructions/agent-worker.md +56 -0
  98. package/instructions/client-session.md +46 -0
  99. package/instructions/framework.md +111 -0
  100. package/instructions/noise-cancellation.md +55 -0
  101. package/package.json +83 -0
@@ -0,0 +1,15 @@
1
+ import { type ResolvedNoiseCancellation, type SerializedNoiseCancellation } from '@plumbus/voice/noise-cancellation';
2
+ /** Trust server-serialized NC from the token route — no @plumbus/core on the browser path. */
3
+ export declare function resolvedNoiseCancellationFromToken(serialized?: SerializedNoiseCancellation): ResolvedNoiseCancellation;
4
+ export declare function applyClientNoiseCancellation(args: {
5
+ localAudioTrack: {
6
+ setProcessor: (processor: unknown) => Promise<void>;
7
+ };
8
+ config: ResolvedNoiseCancellation;
9
+ }): Promise<boolean>;
10
+ export declare function micConstraintsForNoiseCancellation(config: ResolvedNoiseCancellation): {
11
+ echoCancellation: boolean;
12
+ noiseSuppression: boolean;
13
+ autoGainControl: boolean;
14
+ };
15
+ //# sourceMappingURL=client-noise-cancellation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-noise-cancellation.d.ts","sourceRoot":"","sources":["../../src/client/client-noise-cancellation.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,mCAAmC,CAAC;AAE3C,8FAA8F;AAC9F,wBAAgB,kCAAkC,CAChD,UAAU,CAAC,EAAE,2BAA2B,GACvC,yBAAyB,CAe3B;AAED,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,eAAe,EAAE;QACf,YAAY,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACrD,CAAC;IACF,MAAM,EAAE,yBAAyB,CAAC;CACnC,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BnB;AAED,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,yBAAyB,GAAG;IACrF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;CAC1B,CAMA"}
@@ -0,0 +1,54 @@
1
+ import { NoiseCancellationEngine as Engine, NoiseCancellationModel as Model, NoiseCancellationPlacement as Placement, } from '@plumbus/voice/noise-cancellation';
2
+ /** Trust server-serialized NC from the token route — no @plumbus/core on the browser path. */
3
+ export function resolvedNoiseCancellationFromToken(serialized) {
4
+ if (!serialized || serialized.placement === Placement.Off || serialized.engine === 'none') {
5
+ return {
6
+ placement: Placement.Off,
7
+ engine: 'none',
8
+ model: null,
9
+ active: false,
10
+ };
11
+ }
12
+ return {
13
+ placement: serialized.placement,
14
+ engine: serialized.engine,
15
+ model: serialized.model,
16
+ active: true,
17
+ };
18
+ }
19
+ export async function applyClientNoiseCancellation(args) {
20
+ if (args.config.placement !== Placement.Client || !args.config.active) {
21
+ return false;
22
+ }
23
+ if (args.config.engine === Engine.Krisp) {
24
+ try {
25
+ const krisp = await import('@livekit/krisp-noise-filter');
26
+ const useBvc = args.config.model === Model.Bvc;
27
+ const quality = args.config.model === Model.Lite ? 'low' : 'medium';
28
+ await args.localAudioTrack.setProcessor(krisp.KrispNoiseFilter({
29
+ useBVC: useBvc,
30
+ quality,
31
+ }));
32
+ return true;
33
+ }
34
+ catch (error) {
35
+ console.warn('[voice-nc] client Krisp unavailable; continuing without NC', {
36
+ reason: error instanceof Error ? error.message : String(error),
37
+ });
38
+ return false;
39
+ }
40
+ }
41
+ if (args.config.engine === Engine.Rnnoise) {
42
+ console.warn('[voice-nc] client RNNoise is not yet available; continuing without NC');
43
+ return false;
44
+ }
45
+ return false;
46
+ }
47
+ export function micConstraintsForNoiseCancellation(config) {
48
+ return {
49
+ echoCancellation: true,
50
+ noiseSuppression: !config.active,
51
+ autoGainControl: true,
52
+ };
53
+ }
54
+ //# sourceMappingURL=client-noise-cancellation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-noise-cancellation.js","sourceRoot":"","sources":["../../src/client/client-noise-cancellation.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,IAAI,MAAM,EACjC,sBAAsB,IAAI,KAAK,EAC/B,0BAA0B,IAAI,SAAS,GAGxC,MAAM,mCAAmC,CAAC;AAE3C,8FAA8F;AAC9F,MAAM,UAAU,kCAAkC,CAChD,UAAwC;IAExC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,SAAS,KAAK,SAAS,CAAC,GAAG,IAAI,UAAU,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC1F,OAAO;YACL,SAAS,EAAE,SAAS,CAAC,GAAG;YACxB,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,KAAK;SACd,CAAC;IACJ,CAAC;IACD,OAAO;QACL,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,MAAM,EAAE,IAAI;KACb,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,IAKlD;IACC,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACtE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,GAAG,CAAC;YAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,KAAe,CAAC,CAAC,CAAE,QAAkB,CAAC;YAC1F,MAAM,IAAI,CAAC,eAAe,CAAC,YAAY,CACrC,KAAK,CAAC,gBAAgB,CAAC;gBACrB,MAAM,EAAE,MAAM;gBACd,OAAO;aACR,CAAC,CACH,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE;gBACzE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC/D,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;QACtF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,MAAiC;IAKlF,OAAO;QACL,gBAAgB,EAAE,IAAI;QACtB,gBAAgB,EAAE,CAAC,MAAM,CAAC,MAAM;QAChC,eAAe,EAAE,IAAI;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { VoiceEvent } from '@plumbus/voice/provider-kit';
2
+ export interface LiveKitVoiceSessionOptions {
3
+ voiceName: string;
4
+ tokenUrl: string;
5
+ authHeader?: string;
6
+ tokenRequestBody?: Record<string, unknown>;
7
+ onEvent?: (event: VoiceEvent | Record<string, unknown>) => void;
8
+ onAudioChunk?: (chunk: Uint8Array) => void;
9
+ }
10
+ export interface LiveKitVoiceSessionPtt {
11
+ down(): Promise<void>;
12
+ up(payload?: Record<string, unknown>): Promise<void>;
13
+ }
14
+ export interface LiveKitVoiceSession {
15
+ connect(): Promise<void>;
16
+ sendControl(payload: Record<string, unknown>): Promise<void>;
17
+ disconnect(): Promise<void>;
18
+ ptt: LiveKitVoiceSessionPtt;
19
+ }
20
+ export declare function createLiveKitVoiceSession(options: LiveKitVoiceSessionOptions): Promise<LiveKitVoiceSession>;
21
+ //# sourceMappingURL=livekit-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"livekit-session.d.ts","sourceRoot":"","sources":["../../src/client/livekit-session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAc9D,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAChE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,GAAG,EAAE,sBAAsB,CAAC;CAC7B;AAaD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAuN9B"}
@@ -0,0 +1,206 @@
1
+ import { ErrorCode, PlumbusError } from '@plumbus/core/errors';
2
+ import { applyClientNoiseCancellation, micConstraintsForNoiseCancellation, resolvedNoiseCancellationFromToken, } from './client-noise-cancellation.js';
3
+ import { coerceVoiceEvent, isAgentAudioPublication, normalizeBrowserCapturedPcm16, parseLiveKitVoiceDataPayload, resolveAgentAudioTrackName, } from './session-helpers.js';
4
+ export async function createLiveKitVoiceSession(options) {
5
+ let livekit;
6
+ try {
7
+ livekit = await import('livekit-client');
8
+ }
9
+ catch (error) {
10
+ throw new PlumbusError(ErrorCode.DependencyViolation, 'livekit-client is required for createLiveKitVoiceSession — install it as a dependency of your app', { cause: String(error) });
11
+ }
12
+ const tokenResponse = await fetch(options.tokenUrl, {
13
+ method: 'POST',
14
+ headers: {
15
+ 'content-type': 'application/json',
16
+ ...(options.authHeader ? { authorization: options.authHeader } : {}),
17
+ },
18
+ body: JSON.stringify({ voiceName: options.voiceName, ...options.tokenRequestBody }),
19
+ });
20
+ if (!tokenResponse.ok) {
21
+ throw new PlumbusError(ErrorCode.Internal, `Failed to mint LiveKit voice token (${tokenResponse.status})`, { status: tokenResponse.status });
22
+ }
23
+ const body = (await tokenResponse.json());
24
+ const agentAudioTrackName = resolveAgentAudioTrackName(body);
25
+ const noiseCancellation = resolvedNoiseCancellationFromToken(body.noiseCancellation);
26
+ const room = new livekit.Room();
27
+ const audioCaptureCleanups = new Map();
28
+ const attachedAudioElements = new Map();
29
+ let activeAgentAudioTrackSid;
30
+ room.on(livekit.RoomEvent.DataReceived, (payload, _participant, _kind, topic) => {
31
+ const parsed = parseLiveKitVoiceDataPayload(payload);
32
+ const event = coerceVoiceEvent(parsed);
33
+ console.info('[voice-client] data event received', {
34
+ type: typeof event === 'object' && event !== null ? event.type : undefined,
35
+ topic,
36
+ });
37
+ options.onEvent?.(event);
38
+ });
39
+ const releaseAgentAudio = (trackSid) => {
40
+ audioCaptureCleanups.get(trackSid)?.();
41
+ audioCaptureCleanups.delete(trackSid);
42
+ const element = attachedAudioElements.get(trackSid);
43
+ if (element) {
44
+ try {
45
+ element.pause?.();
46
+ }
47
+ catch {
48
+ // ignore
49
+ }
50
+ element.remove?.();
51
+ attachedAudioElements.delete(trackSid);
52
+ console.info('[voice-client] removed agent audio sink', {
53
+ trackSid,
54
+ sinkCount: attachedAudioElements.size,
55
+ });
56
+ }
57
+ if (activeAgentAudioTrackSid === trackSid) {
58
+ activeAgentAudioTrackSid = undefined;
59
+ }
60
+ };
61
+ const removeAllAgentAudioSinks = () => {
62
+ for (const trackSid of [...attachedAudioElements.keys()]) {
63
+ releaseAgentAudio(trackSid);
64
+ }
65
+ for (const cleanup of audioCaptureCleanups.values()) {
66
+ cleanup();
67
+ }
68
+ audioCaptureCleanups.clear();
69
+ activeAgentAudioTrackSid = undefined;
70
+ };
71
+ const subscribeAgentAudio = (track, publication) => {
72
+ if (!livekit.isAudioTrack(track)) {
73
+ console.info('[voice-client] ignored non-audio track', {
74
+ trackName: publication.trackName,
75
+ });
76
+ return;
77
+ }
78
+ if (!isAgentAudioPublication(publication, agentAudioTrackName)) {
79
+ console.info('[voice-client] ignored remote audio track', {
80
+ trackName: publication.trackName,
81
+ expectedTrackName: agentAudioTrackName,
82
+ });
83
+ return;
84
+ }
85
+ if (activeAgentAudioTrackSid === publication.trackSid &&
86
+ attachedAudioElements.has(publication.trackSid)) {
87
+ console.info('[voice-client] ignored duplicate agent audio subscription', {
88
+ trackSid: publication.trackSid,
89
+ });
90
+ return;
91
+ }
92
+ if (attachedAudioElements.size > 0) {
93
+ console.info('[voice-client] removing stale agent audio sinks', {
94
+ count: attachedAudioElements.size,
95
+ });
96
+ removeAllAgentAudioSinks();
97
+ }
98
+ console.info('[voice-client] subscribed to agent audio track', {
99
+ trackName: publication.trackName,
100
+ trackSid: publication.trackSid,
101
+ });
102
+ // Primary, reliable playback path: attach the remote track to a media
103
+ // element so the browser decodes and plays it directly. Web Audio capture
104
+ // of a remote WebRTC track is unreliable across browsers (Chrome only
105
+ // pumps the graph when the track is also sunk to a media element), so it
106
+ // must never be the sole playback path.
107
+ attachedAudioElements.get(publication.trackSid)?.remove?.();
108
+ const element = track.attach();
109
+ element.autoplay = true;
110
+ if ('muted' in element) {
111
+ element.muted = false;
112
+ }
113
+ if (typeof document !== 'undefined' && document.body) {
114
+ element.style.display = 'none';
115
+ document.body.appendChild(element);
116
+ }
117
+ void element.play?.()?.catch?.(() => {
118
+ // Autoplay can be blocked until the next user gesture; the toggle click
119
+ // that starts the session normally satisfies the gesture requirement.
120
+ });
121
+ attachedAudioElements.set(publication.trackSid, element);
122
+ activeAgentAudioTrackSid = publication.trackSid;
123
+ console.info('[voice-client] agent audio sink attached', {
124
+ trackSid: publication.trackSid,
125
+ sinkCount: attachedAudioElements.size,
126
+ });
127
+ // Optional, best-effort PCM hook (e.g. waveform visualization). Never the
128
+ // audible playback path — the capture graph stays muted.
129
+ if (options.onAudioChunk) {
130
+ audioCaptureCleanups.get(publication.trackSid)?.();
131
+ audioCaptureCleanups.set(publication.trackSid, startRemoteAudioPcm16Capture(track, options.onAudioChunk));
132
+ }
133
+ };
134
+ const scanExistingAgentTracks = () => {
135
+ for (const participant of room.remoteParticipants.values()) {
136
+ for (const publication of participant.trackPublications.values()) {
137
+ if (publication.track && publication.isSubscribed) {
138
+ subscribeAgentAudio(publication.track, publication);
139
+ }
140
+ }
141
+ }
142
+ };
143
+ room.on(livekit.RoomEvent.TrackSubscribed, (track, publication) => {
144
+ subscribeAgentAudio(track, publication);
145
+ });
146
+ room.on(livekit.RoomEvent.TrackUnsubscribed, (_track, publication) => {
147
+ releaseAgentAudio(publication.trackSid);
148
+ });
149
+ return {
150
+ async connect() {
151
+ removeAllAgentAudioSinks();
152
+ await room.connect(body.url, body.token);
153
+ const micConstraints = micConstraintsForNoiseCancellation(noiseCancellation);
154
+ await room.localParticipant.setMicrophoneEnabled(true, micConstraints);
155
+ const micPublication = room.localParticipant.getTrackPublication(livekit.Track.Source.Microphone);
156
+ const localAudioTrack = micPublication?.track;
157
+ if (localAudioTrack && 'setProcessor' in localAudioTrack) {
158
+ await applyClientNoiseCancellation({
159
+ localAudioTrack: localAudioTrack,
160
+ config: noiseCancellation,
161
+ });
162
+ }
163
+ scanExistingAgentTracks();
164
+ },
165
+ async sendControl(payload) {
166
+ const encoded = new TextEncoder().encode(JSON.stringify(payload));
167
+ await room.localParticipant.publishData(encoded, { reliable: true });
168
+ },
169
+ async disconnect() {
170
+ removeAllAgentAudioSinks();
171
+ await room.disconnect();
172
+ },
173
+ ptt: {
174
+ async down() {
175
+ const encoded = new TextEncoder().encode(JSON.stringify({ type: 'ptt.down' }));
176
+ await room.localParticipant.publishData(encoded, { reliable: true });
177
+ },
178
+ async up(payload = {}) {
179
+ const encoded = new TextEncoder().encode(JSON.stringify({ type: 'ptt.up', ...payload }));
180
+ await room.localParticipant.publishData(encoded, { reliable: true });
181
+ },
182
+ },
183
+ };
184
+ }
185
+ function startRemoteAudioPcm16Capture(track, onChunk) {
186
+ const audioContext = new AudioContext();
187
+ const source = audioContext.createMediaStreamSource(new MediaStream([track.mediaStreamTrack]));
188
+ const processor = audioContext.createScriptProcessor(4096, 1, 1);
189
+ const silentGain = audioContext.createGain();
190
+ silentGain.gain.value = 0;
191
+ processor.onaudioprocess = (event) => {
192
+ onChunk(normalizeBrowserCapturedPcm16(event.inputBuffer.getChannelData(0), event.inputBuffer.sampleRate));
193
+ };
194
+ source.connect(processor);
195
+ processor.connect(silentGain);
196
+ silentGain.connect(audioContext.destination);
197
+ void audioContext.resume();
198
+ return () => {
199
+ processor.onaudioprocess = null;
200
+ processor.disconnect();
201
+ source.disconnect();
202
+ silentGain.disconnect();
203
+ void audioContext.close();
204
+ };
205
+ }
206
+ //# sourceMappingURL=livekit-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"livekit-session.js","sourceRoot":"","sources":["../../src/client/livekit-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ interface AudioFormatSpec {
2
+ sampleRate: number;
3
+ channels: number;
4
+ }
5
+ export declare function resamplePcm16(audio: Uint8Array, source: AudioFormatSpec, target: AudioFormatSpec): Uint8Array;
6
+ export {};
7
+ //# sourceMappingURL=pcm-resample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pcm-resample.d.ts","sourceRoot":"","sources":["../../src/client/pcm-resample.ts"],"names":[],"mappings":"AAAA,UAAU,eAAe;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,eAAe,GACtB,UAAU,CAaZ"}
@@ -0,0 +1,87 @@
1
+ export function resamplePcm16(audio, source, target) {
2
+ if (source.sampleRate === target.sampleRate && source.channels === target.channels) {
3
+ return audio;
4
+ }
5
+ let samples = bytesToSamples(audio, source.channels);
6
+ if (source.sampleRate !== target.sampleRate) {
7
+ samples = linearResample(samples, source.sampleRate, target.sampleRate, source.channels);
8
+ }
9
+ if (source.channels !== target.channels) {
10
+ samples = convertChannels(samples, source.channels, target.channels);
11
+ }
12
+ return samplesToBytes(samples);
13
+ }
14
+ function bytesToSamples(audio, channels) {
15
+ const sampleCount = Math.floor(audio.byteLength / 2);
16
+ const samples = new Float32Array(sampleCount);
17
+ const view = new DataView(audio.buffer, audio.byteOffset, audio.byteLength);
18
+ for (let index = 0; index < sampleCount; index += 1) {
19
+ samples[index] = view.getInt16(index * 2, true) / 32_768;
20
+ }
21
+ if (channels <= 1) {
22
+ return samples;
23
+ }
24
+ const frames = Math.floor(sampleCount / channels);
25
+ const mono = new Float32Array(frames);
26
+ for (let frame = 0; frame < frames; frame += 1) {
27
+ let sum = 0;
28
+ for (let channel = 0; channel < channels; channel += 1) {
29
+ sum += samples[frame * channels + channel] ?? 0;
30
+ }
31
+ mono[frame] = sum / channels;
32
+ }
33
+ return mono;
34
+ }
35
+ function samplesToBytes(samples) {
36
+ const output = new Uint8Array(samples.length * 2);
37
+ const view = new DataView(output.buffer);
38
+ for (let index = 0; index < samples.length; index += 1) {
39
+ const clamped = Math.max(-1, Math.min(1, samples[index] ?? 0));
40
+ view.setInt16(index * 2, Math.round(clamped * 32_767), true);
41
+ }
42
+ return output;
43
+ }
44
+ function linearResample(samples, sourceRate, targetRate, channels) {
45
+ if (sourceRate === targetRate) {
46
+ return samples;
47
+ }
48
+ const frameCount = Math.max(1, Math.floor(samples.length / Math.max(1, channels)));
49
+ const outputFrames = Math.max(1, Math.round((frameCount * targetRate) / sourceRate));
50
+ const output = new Float32Array(outputFrames * channels);
51
+ const ratio = (frameCount - 1) / Math.max(1, outputFrames - 1);
52
+ for (let outFrame = 0; outFrame < outputFrames; outFrame += 1) {
53
+ const sourcePosition = outFrame * ratio;
54
+ const leftIndex = Math.floor(sourcePosition);
55
+ const rightIndex = Math.min(frameCount - 1, leftIndex + 1);
56
+ const weight = sourcePosition - leftIndex;
57
+ for (let channel = 0; channel < channels; channel += 1) {
58
+ const left = samples[leftIndex * channels + channel] ?? 0;
59
+ const right = samples[rightIndex * channels + channel] ?? 0;
60
+ output[outFrame * channels + channel] = left + (right - left) * weight;
61
+ }
62
+ }
63
+ return output;
64
+ }
65
+ function convertChannels(samples, sourceChannels, targetChannels) {
66
+ if (sourceChannels === targetChannels) {
67
+ return samples;
68
+ }
69
+ const frameCount = Math.floor(samples.length / Math.max(1, sourceChannels));
70
+ const output = new Float32Array(frameCount * targetChannels);
71
+ for (let frame = 0; frame < frameCount; frame += 1) {
72
+ if (targetChannels === 1 && sourceChannels > 1) {
73
+ let sum = 0;
74
+ for (let channel = 0; channel < sourceChannels; channel += 1) {
75
+ sum += samples[frame * sourceChannels + channel] ?? 0;
76
+ }
77
+ output[frame] = sum / sourceChannels;
78
+ continue;
79
+ }
80
+ for (let channel = 0; channel < targetChannels; channel += 1) {
81
+ output[frame * targetChannels + channel] =
82
+ samples[frame * sourceChannels + Math.min(channel, sourceChannels - 1)] ?? 0;
83
+ }
84
+ }
85
+ return output;
86
+ }
87
+ //# sourceMappingURL=pcm-resample.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pcm-resample.js","sourceRoot":"","sources":["../../src/client/pcm-resample.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { VoiceEvent } from '@plumbus/voice/provider-kit';
2
+ export declare const CLIENT_AGENT_AUDIO_FORMAT: {
3
+ sampleRate: number;
4
+ channels: number;
5
+ format: "pcm16";
6
+ };
7
+ export declare const DEFAULT_AGENT_AUDIO_TRACK_NAME = "agent-voice";
8
+ export declare function resolveAgentAudioTrackName(metadata: {
9
+ agentAudioTrackName?: string;
10
+ audioTrackName?: string;
11
+ }): string;
12
+ export declare function parseLiveKitVoiceDataPayload(payload: Uint8Array): unknown;
13
+ export declare function coerceVoiceEvent(value: unknown): VoiceEvent | Record<string, unknown>;
14
+ export declare function float32SamplesToPcm16(samples: Float32Array): Uint8Array;
15
+ export declare function normalizeBrowserCapturedPcm16(samples: Float32Array, sampleRate: number): Uint8Array;
16
+ export declare function isAgentAudioPublication(publication: {
17
+ trackName?: string;
18
+ }, agentTrackName: string): boolean;
19
+ //# sourceMappingURL=session-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-helpers.d.ts","sourceRoot":"","sources":["../../src/client/session-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAG9D,eAAO,MAAM,yBAAyB;;;;CAIrC,CAAC;AAEF,eAAO,MAAM,8BAA8B,gBAAgB,CAAC;AAkB5D,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE;IACnD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAKT;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAUzE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWrF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CAQvE;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,MAAM,GACjB,UAAU,CAMZ;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,cAAc,EAAE,MAAM,GACrB,OAAO,CAET"}
@@ -0,0 +1,70 @@
1
+ import { resamplePcm16 } from './pcm-resample.js';
2
+ export const CLIENT_AGENT_AUDIO_FORMAT = {
3
+ sampleRate: 16_000,
4
+ channels: 1,
5
+ format: 'pcm16',
6
+ };
7
+ export const DEFAULT_AGENT_AUDIO_TRACK_NAME = 'agent-voice';
8
+ const VOICE_EVENT_TYPES = new Set([
9
+ 'session.hello',
10
+ 'session.ready',
11
+ 'stt.partial',
12
+ 'stt.final',
13
+ 'agent.state',
14
+ 'agent.tone',
15
+ 'assistant.delta',
16
+ 'tts.speak',
17
+ 'turn.started',
18
+ 'turn.completed',
19
+ 'turn.interrupted',
20
+ 'turn.failed',
21
+ 'error',
22
+ ]);
23
+ export function resolveAgentAudioTrackName(metadata) {
24
+ const configured = metadata.agentAudioTrackName ?? metadata.audioTrackName;
25
+ return typeof configured === 'string' && configured.length > 0
26
+ ? configured
27
+ : DEFAULT_AGENT_AUDIO_TRACK_NAME;
28
+ }
29
+ export function parseLiveKitVoiceDataPayload(payload) {
30
+ try {
31
+ return JSON.parse(new TextDecoder().decode(payload));
32
+ }
33
+ catch {
34
+ return {
35
+ type: 'error',
36
+ code: 'voice.invalid_message',
37
+ message: 'Expected JSON control frame',
38
+ };
39
+ }
40
+ }
41
+ export function coerceVoiceEvent(value) {
42
+ if (typeof value !== 'object' || value === null) {
43
+ return { type: 'error', code: 'voice.invalid_message', message: 'Expected JSON object' };
44
+ }
45
+ const type = value.type;
46
+ if (typeof type === 'string' && VOICE_EVENT_TYPES.has(type)) {
47
+ return value;
48
+ }
49
+ return value;
50
+ }
51
+ export function float32SamplesToPcm16(samples) {
52
+ const bytes = new Uint8Array(samples.length * 2);
53
+ const view = new DataView(bytes.buffer);
54
+ for (let index = 0; index < samples.length; index += 1) {
55
+ const clamped = Math.max(-1, Math.min(1, samples[index] ?? 0));
56
+ view.setInt16(index * 2, clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff, true);
57
+ }
58
+ return bytes;
59
+ }
60
+ export function normalizeBrowserCapturedPcm16(samples, sampleRate) {
61
+ const pcmAtSourceRate = float32SamplesToPcm16(samples);
62
+ if (sampleRate === CLIENT_AGENT_AUDIO_FORMAT.sampleRate) {
63
+ return pcmAtSourceRate;
64
+ }
65
+ return resamplePcm16(pcmAtSourceRate, { sampleRate, channels: 1 }, CLIENT_AGENT_AUDIO_FORMAT);
66
+ }
67
+ export function isAgentAudioPublication(publication, agentTrackName) {
68
+ return publication.trackName === agentTrackName;
69
+ }
70
+ //# sourceMappingURL=session-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-helpers.js","sourceRoot":"","sources":["../../src/client/session-helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { applyClientNoiseCancellation } from './client/client-noise-cancellation.js';
2
+ export type { LiveKitVoiceSession, LiveKitVoiceSessionOptions, LiveKitVoiceSessionPtt, } from './client/livekit-session.js';
3
+ export { createLiveKitVoiceSession } from './client/livekit-session.js';
4
+ export { CLIENT_AGENT_AUDIO_FORMAT, coerceVoiceEvent, DEFAULT_AGENT_AUDIO_TRACK_NAME, float32SamplesToPcm16, isAgentAudioPublication, normalizeBrowserCapturedPcm16, parseLiveKitVoiceDataPayload, resolveAgentAudioTrackName, } from './client/session-helpers.js';
5
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,8BAA8B,EAC9B,qBAAqB,EACrB,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,4 @@
1
+ export { applyClientNoiseCancellation } from './client/client-noise-cancellation.js';
2
+ export { createLiveKitVoiceSession } from './client/livekit-session.js';
3
+ export { CLIENT_AGENT_AUDIO_FORMAT, coerceVoiceEvent, DEFAULT_AGENT_AUDIO_TRACK_NAME, float32SamplesToPcm16, isAgentAudioPublication, normalizeBrowserCapturedPcm16, parseLiveKitVoiceDataPayload, resolveAgentAudioTrackName, } from './client/session-helpers.js';
4
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAMrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,8BAA8B,EAC9B,qBAAqB,EACrB,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ExecutionContext } from '@plumbus/core';
2
+ export declare function recordLiveKitTransportCost(ctx: Pick<ExecutionContext, 'ai'>, args: {
3
+ sessionId: string;
4
+ connectedAt: Date;
5
+ disconnectedAt: Date;
6
+ participantCount?: number;
7
+ costContext?: Record<string, unknown>;
8
+ }): Promise<void>;
9
+ //# sourceMappingURL=record-livekit-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-livekit-transport.d.ts","sourceRoot":"","sources":["../../src/cost/record-livekit-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,EACjC,IAAI,EAAE;IACJ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,IAAI,CAAC;IAClB,cAAc,EAAE,IAAI,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC,GACA,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
@@ -0,0 +1,29 @@
1
+ import { recordVoiceCost } from '@plumbus/voice';
2
+ import { calculateLiveKitTransportUsd } from '../pricing.js';
3
+ export async function recordLiveKitTransportCost(ctx, args) {
4
+ const wallClockMinutes = Math.max(0, (args.disconnectedAt.getTime() - args.connectedAt.getTime()) / 60_000);
5
+ const participants = args.participantCount ?? 2;
6
+ const cost = calculateLiveKitTransportUsd({
7
+ connectedAt: args.connectedAt,
8
+ disconnectedAt: args.disconnectedAt,
9
+ participantCount: participants,
10
+ });
11
+ await recordVoiceCost(ctx, {
12
+ operation: 'transport',
13
+ provider: 'livekit',
14
+ model: 'livekit-cloud',
15
+ mediaUsage: {
16
+ connectionMinutes: wallClockMinutes,
17
+ participantMinutes: wallClockMinutes * participants,
18
+ },
19
+ cost,
20
+ latencyMs: 0,
21
+ costContext: {
22
+ serviceArea: 'voice',
23
+ operationName: 'voice.transport',
24
+ relatedEntityId: args.sessionId,
25
+ ...args.costContext,
26
+ },
27
+ });
28
+ }
29
+ //# sourceMappingURL=record-livekit-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-livekit-transport.js","sourceRoot":"","sources":["../../src/cost/record-livekit-transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,GAAiC,EACjC,IAMC;IAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAC/B,CAAC,EACD,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CACtE,CAAC;IACF,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,4BAA4B,CAAC;QACxC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,gBAAgB,EAAE,YAAY;KAC/B,CAAC,CAAC;IAEH,MAAM,eAAe,CAAC,GAAG,EAAE;QACzB,SAAS,EAAE,WAAW;QACtB,QAAQ,EAAE,SAAS;QACnB,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE;YACV,iBAAiB,EAAE,gBAAgB;YACnC,kBAAkB,EAAE,gBAAgB,GAAG,YAAY;SACpD;QACD,IAAI;QACJ,SAAS,EAAE,CAAC;QACZ,WAAW,EAAE;YACX,WAAW,EAAE,OAAO;YACpB,aAAa,EAAE,iBAAiB;YAChC,eAAe,EAAE,IAAI,CAAC,SAAS;YAC/B,GAAG,IAAI,CAAC,WAAW;SACpB;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { VoiceProviderCredentials } from '@plumbus/voice/provider-kit';
2
+ export declare function resolveCredentialsFromEnv(env?: NodeJS.ProcessEnv): VoiceProviderCredentials;
3
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAE5E,wBAAgB,yBAAyB,CACvC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,wBAAwB,CAM1B"}
@@ -0,0 +1,8 @@
1
+ export function resolveCredentialsFromEnv(env = process.env) {
2
+ return {
3
+ url: env.LIVEKIT_URL,
4
+ apiKey: env.LIVEKIT_API_KEY,
5
+ apiSecret: env.LIVEKIT_API_SECRET,
6
+ };
7
+ }
8
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,yBAAyB,CACvC,MAAyB,OAAO,CAAC,GAAG;IAEpC,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,WAAW;QACpB,MAAM,EAAE,GAAG,CAAC,eAAe;QAC3B,SAAS,EAAE,GAAG,CAAC,kBAAkB;KAClC,CAAC;AACJ,CAAC"}