@memberjunction/livekit-room-server 6.2.0-edge.1 → 6.2.0-edge.3
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/README.md +85 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/livekit-agent-room-coordinator.d.ts +105 -8
- package/dist/livekit-agent-room-coordinator.d.ts.map +1 -1
- package/dist/livekit-agent-room-coordinator.js +125 -25
- package/dist/livekit-agent-room-coordinator.js.map +1 -1
- package/dist/livekit-egress-service.d.ts +7 -0
- package/dist/livekit-egress-service.d.ts.map +1 -1
- package/dist/livekit-egress-service.js +11 -0
- package/dist/livekit-egress-service.js.map +1 -1
- package/dist/livekit-native-module.d.ts +18 -0
- package/dist/livekit-native-module.d.ts.map +1 -0
- package/dist/livekit-native-module.js +20 -0
- package/dist/livekit-native-module.js.map +1 -0
- package/dist/livekit-sip-service.d.ts +141 -0
- package/dist/livekit-sip-service.d.ts.map +1 -0
- package/dist/livekit-sip-service.js +169 -0
- package/dist/livekit-sip-service.js.map +1 -0
- package/dist/livekit-token-service.d.ts +6 -0
- package/dist/livekit-token-service.d.ts.map +1 -1
- package/dist/livekit-token-service.js +8 -0
- package/dist/livekit-token-service.js.map +1 -1
- package/dist/livekit-webhook.d.ts +65 -0
- package/dist/livekit-webhook.d.ts.map +1 -0
- package/dist/livekit-webhook.js +65 -0
- package/dist/livekit-webhook.js.map +1 -0
- package/dist/meeting-dial-in-service.d.ts +75 -0
- package/dist/meeting-dial-in-service.d.ts.map +1 -0
- package/dist/meeting-dial-in-service.js +175 -0
- package/dist/meeting-dial-in-service.js.map +1 -0
- package/dist/room-audio/audio-decoder.d.ts +40 -0
- package/dist/room-audio/audio-decoder.d.ts.map +1 -0
- package/dist/room-audio/audio-decoder.js +205 -0
- package/dist/room-audio/audio-decoder.js.map +1 -0
- package/dist/room-audio/comfort-tone.d.ts +23 -0
- package/dist/room-audio/comfort-tone.d.ts.map +1 -0
- package/dist/room-audio/comfort-tone.js +70 -0
- package/dist/room-audio/comfort-tone.js.map +1 -0
- package/dist/room-audio/pcm-frame-pump.d.ts +134 -0
- package/dist/room-audio/pcm-frame-pump.d.ts.map +1 -0
- package/dist/room-audio/pcm-frame-pump.js +253 -0
- package/dist/room-audio/pcm-frame-pump.js.map +1 -0
- package/dist/room-audio/room-audio-decode-cache.d.ts +37 -0
- package/dist/room-audio/room-audio-decode-cache.d.ts.map +1 -0
- package/dist/room-audio/room-audio-decode-cache.js +75 -0
- package/dist/room-audio/room-audio-decode-cache.js.map +1 -0
- package/dist/room-audio/room-audio-player.d.ts +178 -0
- package/dist/room-audio/room-audio-player.d.ts.map +1 -0
- package/dist/room-audio/room-audio-player.js +404 -0
- package/dist/room-audio/room-audio-player.js.map +1 -0
- package/dist/room-audio/room-audio-sources.d.ts +47 -0
- package/dist/room-audio/room-audio-sources.d.ts.map +1 -0
- package/dist/room-audio/room-audio-sources.js +116 -0
- package/dist/room-audio/room-audio-sources.js.map +1 -0
- package/dist/room-audio/room-speech-synthesizer.d.ts +55 -0
- package/dist/room-audio/room-speech-synthesizer.d.ts.map +1 -0
- package/dist/room-audio/room-speech-synthesizer.js +56 -0
- package/dist/room-audio/room-speech-synthesizer.js.map +1 -0
- package/dist/room-authorization.d.ts +58 -0
- package/dist/room-authorization.d.ts.map +1 -0
- package/dist/room-authorization.js +212 -0
- package/dist/room-authorization.js.map +1 -0
- package/dist/room-handoff/handoff-offer-registry.d.ts +137 -0
- package/dist/room-handoff/handoff-offer-registry.d.ts.map +1 -0
- package/dist/room-handoff/handoff-offer-registry.js +521 -0
- package/dist/room-handoff/handoff-offer-registry.js.map +1 -0
- package/dist/room-handoff/handoff-types.d.ts +170 -0
- package/dist/room-handoff/handoff-types.d.ts.map +1 -0
- package/dist/room-handoff/handoff-types.js +16 -0
- package/dist/room-handoff/handoff-types.js.map +1 -0
- package/dist/room-handoff/room-handoff-engine.d.ts +141 -0
- package/dist/room-handoff/room-handoff-engine.d.ts.map +1 -0
- package/dist/room-handoff/room-handoff-engine.js +530 -0
- package/dist/room-handoff/room-handoff-engine.js.map +1 -0
- package/package.json +14 -9
|
@@ -64,6 +64,17 @@ export class LiveKitEgressService {
|
|
|
64
64
|
const list = await this.client.listEgress({ roomName, active: true });
|
|
65
65
|
return list.map((info) => this.toRecordingInfo(info, roomName));
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Looks up the room name for an egress id.
|
|
69
|
+
*
|
|
70
|
+
* @param egressId The egress id to query.
|
|
71
|
+
* @returns The room name, or `undefined` if not found.
|
|
72
|
+
*/
|
|
73
|
+
async GetRoomForEgress(egressId) {
|
|
74
|
+
const list = await this.client.listEgress({ egressId });
|
|
75
|
+
const info = list?.[0];
|
|
76
|
+
return info?.roomName || undefined;
|
|
77
|
+
}
|
|
67
78
|
/** Maps an SDK `EgressInfo` to the normalized {@link RecordingInfo}. */
|
|
68
79
|
toRecordingInfo(info, roomName) {
|
|
69
80
|
const result = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"livekit-egress-service.js","sourceRoot":"","sources":["../src/livekit-egress-service.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"livekit-egress-service.js","sourceRoot":"","sources":["../src/livekit-egress-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Where a server-side LiveKit bot (an agent voice, a hold-music player) finds the native room-client
|
|
3
|
+
* wrapper it joins a room with. One resolution rule for every bot in this package, so an operator who points
|
|
4
|
+
* `LIVEKIT_NATIVE_MODULE` at a custom wrapper changes all of them at once.
|
|
5
|
+
*
|
|
6
|
+
* @module @memberjunction/livekit-room-server
|
|
7
|
+
*/
|
|
8
|
+
/** The default native room-client wrapper — the `@livekit/rtc-node`-backed package this repo ships. */
|
|
9
|
+
export declare const DEFAULT_LIVEKIT_NATIVE_MODULE = "@memberjunction/ai-bridge-livekit-native";
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the module specifier of the native LiveKit room-client wrapper: an explicit override (tests, a host that
|
|
12
|
+
* wires its own), else the `LIVEKIT_NATIVE_MODULE` environment variable (e.g. a deployment's custom-sample-rate
|
|
13
|
+
* wrapper), else {@link DEFAULT_LIVEKIT_NATIVE_MODULE}.
|
|
14
|
+
*
|
|
15
|
+
* @param override An explicit specifier that wins over the environment and the default.
|
|
16
|
+
*/
|
|
17
|
+
export declare function ResolveLiveKitNativeModuleSpecifier(override?: string): string;
|
|
18
|
+
//# sourceMappingURL=livekit-native-module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"livekit-native-module.d.ts","sourceRoot":"","sources":["../src/livekit-native-module.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uGAAuG;AACvG,eAAO,MAAM,6BAA6B,6CAA6C,CAAC;AAExF;;;;;;GAMG;AACH,wBAAgB,mCAAmC,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7E"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Where a server-side LiveKit bot (an agent voice, a hold-music player) finds the native room-client
|
|
3
|
+
* wrapper it joins a room with. One resolution rule for every bot in this package, so an operator who points
|
|
4
|
+
* `LIVEKIT_NATIVE_MODULE` at a custom wrapper changes all of them at once.
|
|
5
|
+
*
|
|
6
|
+
* @module @memberjunction/livekit-room-server
|
|
7
|
+
*/
|
|
8
|
+
/** The default native room-client wrapper — the `@livekit/rtc-node`-backed package this repo ships. */
|
|
9
|
+
export const DEFAULT_LIVEKIT_NATIVE_MODULE = '@memberjunction/ai-bridge-livekit-native';
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the module specifier of the native LiveKit room-client wrapper: an explicit override (tests, a host that
|
|
12
|
+
* wires its own), else the `LIVEKIT_NATIVE_MODULE` environment variable (e.g. a deployment's custom-sample-rate
|
|
13
|
+
* wrapper), else {@link DEFAULT_LIVEKIT_NATIVE_MODULE}.
|
|
14
|
+
*
|
|
15
|
+
* @param override An explicit specifier that wins over the environment and the default.
|
|
16
|
+
*/
|
|
17
|
+
export function ResolveLiveKitNativeModuleSpecifier(override) {
|
|
18
|
+
return override ?? process.env.LIVEKIT_NATIVE_MODULE ?? DEFAULT_LIVEKIT_NATIVE_MODULE;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=livekit-native-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"livekit-native-module.js","sourceRoot":"","sources":["../src/livekit-native-module.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uGAAuG;AACvG,MAAM,CAAC,MAAM,6BAA6B,GAAG,0CAA0C,CAAC;AAExF;;;;;;GAMG;AACH,MAAM,UAAU,mCAAmC,CAAC,QAAiB;IACnE,OAAO,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,6BAA6B,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview {@link LiveKitSipService}: the server-side LiveKit SIP and room-presence operations MJ needs to put
|
|
3
|
+
* phone calls into rooms.
|
|
4
|
+
*
|
|
5
|
+
* LiveKit SIP is the SIP gateway in front of a LiveKit deployment. A carrier's SIP trunk (Twilio Elastic SIP Trunking,
|
|
6
|
+
* Telnyx, …) points at it; an **inbound trunk** says which numbers LiveKit accepts calls for and a **dispatch rule** says
|
|
7
|
+
* which room an accepted call lands in. This service wraps the three things MJ does with it:
|
|
8
|
+
*
|
|
9
|
+
* - **inbound routing**: make sure an inbound trunk and an `individual` dispatch rule exist, so a call to one of the
|
|
10
|
+
* configured numbers lands in a fresh room named `<prefix>…` (idempotent: it finds before it creates);
|
|
11
|
+
* - **dial out**: `createSipParticipant`, which makes the room call a phone number and adds that call to the room as a
|
|
12
|
+
* participant (an outbound call, a fallback leg, a blind transfer to a number);
|
|
13
|
+
* - **presence**: whether a given participant is currently in a room, and removing one (hanging up a SIP leg).
|
|
14
|
+
*
|
|
15
|
+
* The SDK clients are injected (`SipClientLike`, `RoomServiceClientLike`) so the logic is unit-tested without a network.
|
|
16
|
+
* Credentials come from the same `LIVEKIT_URL` / `LIVEKIT_API_KEY` / `LIVEKIT_API_SECRET` the token service uses.
|
|
17
|
+
*
|
|
18
|
+
* @module @memberjunction/livekit-room-server
|
|
19
|
+
*/
|
|
20
|
+
import { RoomServiceClient, SipClient } from 'livekit-server-sdk';
|
|
21
|
+
import { type LiveKitServerConfig } from './livekit-token-service.js';
|
|
22
|
+
/** The `ParticipantInfo.Kind` value LiveKit gives a SIP participant (`ParticipantInfo.Kind.SIP`). */
|
|
23
|
+
export declare const LIVEKIT_PARTICIPANT_KIND_SIP = 3;
|
|
24
|
+
/** Participant attributes LiveKit SIP sets on a phone participant. */
|
|
25
|
+
export declare const LIVEKIT_SIP_ATTRIBUTES: {
|
|
26
|
+
/** The caller's number. */
|
|
27
|
+
readonly CallerNumber: "sip.phoneNumber";
|
|
28
|
+
/** The number that was dialed (the trunk number). */
|
|
29
|
+
readonly DialedNumber: "sip.trunkPhoneNumber";
|
|
30
|
+
readonly CallStatus: "sip.callStatus";
|
|
31
|
+
readonly CallID: "sip.callID";
|
|
32
|
+
};
|
|
33
|
+
/** How long the LiveKit API is given to answer a dial request that waits for the call to be answered. */
|
|
34
|
+
export declare const SIP_DIAL_REQUEST_TIMEOUT_SECONDS = 60;
|
|
35
|
+
/** The longest one dialed call may last. A safety ceiling; the call's own duration cap is enforced by MJ. */
|
|
36
|
+
export declare const SIP_DIAL_MAX_DURATION_SECONDS = 3600;
|
|
37
|
+
/** The SIP operations the service uses (a `Pick` so tests inject a fake). */
|
|
38
|
+
export type SipClientLike = Pick<SipClient, 'createSipParticipant' | 'listSipInboundTrunk' | 'listSipOutboundTrunk' | 'createSipInboundTrunk' | 'listSipDispatchRule' | 'createSipDispatchRule'>;
|
|
39
|
+
/** The room operations the service uses (a `Pick` so tests inject a fake). */
|
|
40
|
+
export type RoomServiceClientLike = Pick<RoomServiceClient, 'listParticipants' | 'removeParticipant'>;
|
|
41
|
+
/** One participant in a room, reduced to what MJ looks at. */
|
|
42
|
+
export interface RoomParticipantSummary {
|
|
43
|
+
Identity: string;
|
|
44
|
+
Name: string;
|
|
45
|
+
/** True for a phone (SIP) participant. */
|
|
46
|
+
IsSip: boolean;
|
|
47
|
+
/** True once the participant's media connection is established. */
|
|
48
|
+
IsConnected: boolean;
|
|
49
|
+
Attributes: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
/** A SIP trunk, reduced to what MJ validates and reports. */
|
|
52
|
+
export interface SipTrunkSummary {
|
|
53
|
+
TrunkID: string;
|
|
54
|
+
Name: string;
|
|
55
|
+
Numbers: string[];
|
|
56
|
+
}
|
|
57
|
+
/** What a dial-out needs. */
|
|
58
|
+
export interface DialSipParticipantParams {
|
|
59
|
+
RoomName: string;
|
|
60
|
+
/** The outbound trunk to dial through. */
|
|
61
|
+
TrunkID: string;
|
|
62
|
+
/** The number to dial, E.164. */
|
|
63
|
+
Number: string;
|
|
64
|
+
/** The identity the call joins the room with (stable, so presence can be checked). */
|
|
65
|
+
ParticipantIdentity: string;
|
|
66
|
+
DisplayName?: string;
|
|
67
|
+
/** The caller ID to present. Defaults to the trunk's number. */
|
|
68
|
+
FromNumber?: string;
|
|
69
|
+
/** Seconds the number may ring. */
|
|
70
|
+
RingTimeoutSeconds?: number;
|
|
71
|
+
/** Block until the call is answered (default true). */
|
|
72
|
+
WaitUntilAnswered?: boolean;
|
|
73
|
+
}
|
|
74
|
+
/** What inbound routing needs. */
|
|
75
|
+
export interface EnsureInboundRoutingParams {
|
|
76
|
+
/** The numbers LiveKit accepts calls for. */
|
|
77
|
+
Numbers: string[];
|
|
78
|
+
/** Calls land in a room whose name starts with this. */
|
|
79
|
+
RoomPrefix: string;
|
|
80
|
+
/** A name for anything created. */
|
|
81
|
+
Name?: string;
|
|
82
|
+
/** Source addresses allowed to call the trunk (the carrier's signalling addresses). Empty allows any. */
|
|
83
|
+
AllowedAddresses?: string[];
|
|
84
|
+
/** SIP digest credentials the carrier must present, when the trunk uses them. */
|
|
85
|
+
AuthUsername?: string;
|
|
86
|
+
AuthPassword?: string;
|
|
87
|
+
}
|
|
88
|
+
/** The outcome of {@link LiveKitSipService.EnsureInboundRouting}. */
|
|
89
|
+
export interface InboundRoutingResult {
|
|
90
|
+
TrunkID: string;
|
|
91
|
+
DispatchRuleID: string;
|
|
92
|
+
/** True when this call created the trunk. */
|
|
93
|
+
CreatedTrunk: boolean;
|
|
94
|
+
/** True when this call created the dispatch rule. */
|
|
95
|
+
CreatedDispatchRule: boolean;
|
|
96
|
+
}
|
|
97
|
+
/** SIP and presence operations against a LiveKit deployment. */
|
|
98
|
+
export declare class LiveKitSipService {
|
|
99
|
+
private readonly config;
|
|
100
|
+
private sip?;
|
|
101
|
+
private rooms?;
|
|
102
|
+
/**
|
|
103
|
+
* @param config Explicit credentials; omitted fields fall back to the `LIVEKIT_*` environment variables.
|
|
104
|
+
* @param sip Injectable SIP client (tests); defaults to a real `SipClient`, built on first use.
|
|
105
|
+
* @param rooms Injectable room-service client (tests); defaults to a real `RoomServiceClient`, built on first use.
|
|
106
|
+
*/
|
|
107
|
+
constructor(config?: Partial<LiveKitServerConfig>, sip?: SipClientLike, rooms?: RoomServiceClientLike);
|
|
108
|
+
/** Whether the LiveKit credentials needed to call the API are present. */
|
|
109
|
+
get IsConfigured(): boolean;
|
|
110
|
+
/** The participants currently in a room. A room that does not exist (nobody has joined) has none. */
|
|
111
|
+
ListParticipants(roomName: string): Promise<RoomParticipantSummary[]>;
|
|
112
|
+
/** Whether a participant with this identity is in the room with an established connection. */
|
|
113
|
+
IsParticipantPresent(roomName: string, identity: string): Promise<boolean>;
|
|
114
|
+
/** Removes a participant from a room (hangs up a phone leg). */
|
|
115
|
+
RemoveParticipant(roomName: string, identity: string): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Calls a number and adds the call to the room. By default the request blocks until the call is answered, so a
|
|
118
|
+
* resolved promise means the other side picked up; a throw means it did not (busy, no answer, refused).
|
|
119
|
+
*/
|
|
120
|
+
DialIntoRoom(params: DialSipParticipantParams): Promise<void>;
|
|
121
|
+
/** The inbound trunks LiveKit has, optionally only those that include one of the numbers. */
|
|
122
|
+
ListInboundTrunks(numbers?: string[]): Promise<SipTrunkSummary[]>;
|
|
123
|
+
/** The outbound trunks LiveKit has. */
|
|
124
|
+
ListOutboundTrunks(): Promise<SipTrunkSummary[]>;
|
|
125
|
+
/** Whether the outbound trunk id exists on this LiveKit deployment. */
|
|
126
|
+
OutboundTrunkExists(trunkID: string): Promise<boolean>;
|
|
127
|
+
/**
|
|
128
|
+
* Makes sure calls to `Numbers` land in a new room named `<RoomPrefix>…`: finds (or creates) the inbound trunk for
|
|
129
|
+
* the numbers and an `individual` dispatch rule with the prefix on it. Safe to run on every start: it only creates
|
|
130
|
+
* what is missing.
|
|
131
|
+
*/
|
|
132
|
+
EnsureInboundRouting(params: EnsureInboundRoutingParams): Promise<InboundRoutingResult>;
|
|
133
|
+
private createInboundTrunk;
|
|
134
|
+
/** The id of an `individual` dispatch rule on the trunk whose room prefix matches, if there is one. */
|
|
135
|
+
private findDispatchRule;
|
|
136
|
+
private sipClient;
|
|
137
|
+
private roomClient;
|
|
138
|
+
/** The HTTP(S) host and API credentials, or a clear error when they are not configured. */
|
|
139
|
+
private credentials;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=livekit-sip-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"livekit-sip-service.d.ts","sourceRoot":"","sources":["../src/livekit-sip-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview {@link LiveKitSipService}: the server-side LiveKit SIP and room-presence operations MJ needs to put
|
|
3
|
+
* phone calls into rooms.
|
|
4
|
+
*
|
|
5
|
+
* LiveKit SIP is the SIP gateway in front of a LiveKit deployment. A carrier's SIP trunk (Twilio Elastic SIP Trunking,
|
|
6
|
+
* Telnyx, …) points at it; an **inbound trunk** says which numbers LiveKit accepts calls for and a **dispatch rule** says
|
|
7
|
+
* which room an accepted call lands in. This service wraps the three things MJ does with it:
|
|
8
|
+
*
|
|
9
|
+
* - **inbound routing**: make sure an inbound trunk and an `individual` dispatch rule exist, so a call to one of the
|
|
10
|
+
* configured numbers lands in a fresh room named `<prefix>…` (idempotent: it finds before it creates);
|
|
11
|
+
* - **dial out**: `createSipParticipant`, which makes the room call a phone number and adds that call to the room as a
|
|
12
|
+
* participant (an outbound call, a fallback leg, a blind transfer to a number);
|
|
13
|
+
* - **presence**: whether a given participant is currently in a room, and removing one (hanging up a SIP leg).
|
|
14
|
+
*
|
|
15
|
+
* The SDK clients are injected (`SipClientLike`, `RoomServiceClientLike`) so the logic is unit-tested without a network.
|
|
16
|
+
* Credentials come from the same `LIVEKIT_URL` / `LIVEKIT_API_KEY` / `LIVEKIT_API_SECRET` the token service uses.
|
|
17
|
+
*
|
|
18
|
+
* @module @memberjunction/livekit-room-server
|
|
19
|
+
*/
|
|
20
|
+
import { ParticipantInfo_State, RoomServiceClient, SipClient } from 'livekit-server-sdk';
|
|
21
|
+
import { LogStatus } from '@memberjunction/core';
|
|
22
|
+
import { LiveKitTokenService } from './livekit-token-service.js';
|
|
23
|
+
import { WsToHttpUrl } from './livekit-egress-service.js';
|
|
24
|
+
/** The `ParticipantInfo.Kind` value LiveKit gives a SIP participant (`ParticipantInfo.Kind.SIP`). */
|
|
25
|
+
export const LIVEKIT_PARTICIPANT_KIND_SIP = 3;
|
|
26
|
+
/** Participant attributes LiveKit SIP sets on a phone participant. */
|
|
27
|
+
export const LIVEKIT_SIP_ATTRIBUTES = {
|
|
28
|
+
/** The caller's number. */
|
|
29
|
+
CallerNumber: 'sip.phoneNumber',
|
|
30
|
+
/** The number that was dialed (the trunk number). */
|
|
31
|
+
DialedNumber: 'sip.trunkPhoneNumber',
|
|
32
|
+
CallStatus: 'sip.callStatus',
|
|
33
|
+
CallID: 'sip.callID',
|
|
34
|
+
};
|
|
35
|
+
/** How long the LiveKit API is given to answer a dial request that waits for the call to be answered. */
|
|
36
|
+
export const SIP_DIAL_REQUEST_TIMEOUT_SECONDS = 60;
|
|
37
|
+
/** The longest one dialed call may last. A safety ceiling; the call's own duration cap is enforced by MJ. */
|
|
38
|
+
export const SIP_DIAL_MAX_DURATION_SECONDS = 3600;
|
|
39
|
+
/** SIP and presence operations against a LiveKit deployment. */
|
|
40
|
+
export class LiveKitSipService {
|
|
41
|
+
/**
|
|
42
|
+
* @param config Explicit credentials; omitted fields fall back to the `LIVEKIT_*` environment variables.
|
|
43
|
+
* @param sip Injectable SIP client (tests); defaults to a real `SipClient`, built on first use.
|
|
44
|
+
* @param rooms Injectable room-service client (tests); defaults to a real `RoomServiceClient`, built on first use.
|
|
45
|
+
*/
|
|
46
|
+
constructor(config, sip, rooms) {
|
|
47
|
+
this.config = config ?? {};
|
|
48
|
+
this.sip = sip;
|
|
49
|
+
this.rooms = rooms;
|
|
50
|
+
}
|
|
51
|
+
/** Whether the LiveKit credentials needed to call the API are present. */
|
|
52
|
+
get IsConfigured() {
|
|
53
|
+
return new LiveKitTokenService(this.config).IsConfigured;
|
|
54
|
+
}
|
|
55
|
+
// ── presence ─────────────────────────────────────────────────────────────────
|
|
56
|
+
/** The participants currently in a room. A room that does not exist (nobody has joined) has none. */
|
|
57
|
+
async ListParticipants(roomName) {
|
|
58
|
+
const participants = await this.roomClient().listParticipants(roomName);
|
|
59
|
+
return participants.map((p) => ({
|
|
60
|
+
Identity: p.identity,
|
|
61
|
+
Name: p.name,
|
|
62
|
+
IsSip: p.kind === LIVEKIT_PARTICIPANT_KIND_SIP,
|
|
63
|
+
IsConnected: p.state === ParticipantInfo_State.JOINED || p.state === ParticipantInfo_State.ACTIVE,
|
|
64
|
+
Attributes: { ...p.attributes },
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
/** Whether a participant with this identity is in the room with an established connection. */
|
|
68
|
+
async IsParticipantPresent(roomName, identity) {
|
|
69
|
+
const wanted = identity.toLowerCase();
|
|
70
|
+
const participants = await this.ListParticipants(roomName);
|
|
71
|
+
return participants.some((p) => p.IsConnected && p.Identity.toLowerCase() === wanted);
|
|
72
|
+
}
|
|
73
|
+
/** Removes a participant from a room (hangs up a phone leg). */
|
|
74
|
+
async RemoveParticipant(roomName, identity) {
|
|
75
|
+
await this.roomClient().removeParticipant(roomName, identity);
|
|
76
|
+
}
|
|
77
|
+
// ── dial out ─────────────────────────────────────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* Calls a number and adds the call to the room. By default the request blocks until the call is answered, so a
|
|
80
|
+
* resolved promise means the other side picked up; a throw means it did not (busy, no answer, refused).
|
|
81
|
+
*/
|
|
82
|
+
async DialIntoRoom(params) {
|
|
83
|
+
await this.sipClient().createSipParticipant(params.TrunkID, params.Number, params.RoomName, {
|
|
84
|
+
participantIdentity: params.ParticipantIdentity,
|
|
85
|
+
participantName: params.DisplayName,
|
|
86
|
+
fromNumber: params.FromNumber,
|
|
87
|
+
ringingTimeout: params.RingTimeoutSeconds,
|
|
88
|
+
maxCallDuration: SIP_DIAL_MAX_DURATION_SECONDS,
|
|
89
|
+
waitUntilAnswered: params.WaitUntilAnswered ?? true,
|
|
90
|
+
timeout: SIP_DIAL_REQUEST_TIMEOUT_SECONDS,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// ── trunks and routing ───────────────────────────────────────────────────────
|
|
94
|
+
/** The inbound trunks LiveKit has, optionally only those that include one of the numbers. */
|
|
95
|
+
async ListInboundTrunks(numbers) {
|
|
96
|
+
const trunks = await this.sipClient().listSipInboundTrunk(numbers?.length ? { numbers } : undefined);
|
|
97
|
+
return trunks.map((t) => ({ TrunkID: t.sipTrunkId, Name: t.name, Numbers: [...t.numbers] }));
|
|
98
|
+
}
|
|
99
|
+
/** The outbound trunks LiveKit has. */
|
|
100
|
+
async ListOutboundTrunks() {
|
|
101
|
+
const trunks = await this.sipClient().listSipOutboundTrunk();
|
|
102
|
+
return trunks.map((t) => ({ TrunkID: t.sipTrunkId, Name: t.name, Numbers: [...t.numbers] }));
|
|
103
|
+
}
|
|
104
|
+
/** Whether the outbound trunk id exists on this LiveKit deployment. */
|
|
105
|
+
async OutboundTrunkExists(trunkID) {
|
|
106
|
+
const trunks = await this.ListOutboundTrunks();
|
|
107
|
+
return trunks.some((t) => t.TrunkID === trunkID);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Makes sure calls to `Numbers` land in a new room named `<RoomPrefix>…`: finds (or creates) the inbound trunk for
|
|
111
|
+
* the numbers and an `individual` dispatch rule with the prefix on it. Safe to run on every start: it only creates
|
|
112
|
+
* what is missing.
|
|
113
|
+
*/
|
|
114
|
+
async EnsureInboundRouting(params) {
|
|
115
|
+
const name = params.Name ?? 'MemberJunction inbound';
|
|
116
|
+
const existingTrunk = (await this.ListInboundTrunks(params.Numbers)).find((t) => params.Numbers.every((n) => t.Numbers.includes(n)));
|
|
117
|
+
const trunk = existingTrunk ?? (await this.createInboundTrunk(name, params));
|
|
118
|
+
const rule = await this.findDispatchRule(trunk.TrunkID, params.RoomPrefix);
|
|
119
|
+
if (rule) {
|
|
120
|
+
return { TrunkID: trunk.TrunkID, DispatchRuleID: rule, CreatedTrunk: !existingTrunk, CreatedDispatchRule: false };
|
|
121
|
+
}
|
|
122
|
+
const created = await this.sipClient().createSipDispatchRule({ type: 'individual', roomPrefix: params.RoomPrefix }, { name: `${name} dispatch`, trunkIds: [trunk.TrunkID] });
|
|
123
|
+
LogStatus(`[LiveKitSip] created dispatch rule ${created.sipDispatchRuleId} (rooms '${params.RoomPrefix}…') on trunk ${trunk.TrunkID}.`);
|
|
124
|
+
return { TrunkID: trunk.TrunkID, DispatchRuleID: created.sipDispatchRuleId, CreatedTrunk: !existingTrunk, CreatedDispatchRule: true };
|
|
125
|
+
}
|
|
126
|
+
async createInboundTrunk(name, params) {
|
|
127
|
+
const created = await this.sipClient().createSipInboundTrunk(name, params.Numbers, {
|
|
128
|
+
allowedAddresses: params.AllowedAddresses?.length ? params.AllowedAddresses : undefined,
|
|
129
|
+
authUsername: params.AuthUsername,
|
|
130
|
+
authPassword: params.AuthPassword,
|
|
131
|
+
});
|
|
132
|
+
LogStatus(`[LiveKitSip] created inbound trunk ${created.sipTrunkId} for ${params.Numbers.length} number(s).`);
|
|
133
|
+
return { TrunkID: created.sipTrunkId, Name: created.name, Numbers: [...created.numbers] };
|
|
134
|
+
}
|
|
135
|
+
/** The id of an `individual` dispatch rule on the trunk whose room prefix matches, if there is one. */
|
|
136
|
+
async findDispatchRule(trunkID, roomPrefix) {
|
|
137
|
+
const rules = await this.sipClient().listSipDispatchRule({ trunkIds: [trunkID] });
|
|
138
|
+
const match = rules.find((r) => r.rule?.rule.case === 'dispatchRuleIndividual' && r.rule.rule.value.roomPrefix === roomPrefix);
|
|
139
|
+
return match?.sipDispatchRuleId;
|
|
140
|
+
}
|
|
141
|
+
// ── clients ──────────────────────────────────────────────────────────────────
|
|
142
|
+
sipClient() {
|
|
143
|
+
if (!this.sip) {
|
|
144
|
+
const { host, apiKey, apiSecret } = this.credentials();
|
|
145
|
+
this.sip = new SipClient(host, apiKey, apiSecret);
|
|
146
|
+
}
|
|
147
|
+
return this.sip;
|
|
148
|
+
}
|
|
149
|
+
roomClient() {
|
|
150
|
+
if (!this.rooms) {
|
|
151
|
+
const { host, apiKey, apiSecret } = this.credentials();
|
|
152
|
+
this.rooms = new RoomServiceClient(host, apiKey, apiSecret);
|
|
153
|
+
}
|
|
154
|
+
return this.rooms;
|
|
155
|
+
}
|
|
156
|
+
/** The HTTP(S) host and API credentials, or a clear error when they are not configured. */
|
|
157
|
+
credentials() {
|
|
158
|
+
const tokens = new LiveKitTokenService(this.config);
|
|
159
|
+
if (!tokens.IsConfigured) {
|
|
160
|
+
throw new Error('LiveKit is not configured. Set LIVEKIT_URL, LIVEKIT_API_KEY and LIVEKIT_API_SECRET.');
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
host: WsToHttpUrl(tokens.ServerUrl),
|
|
164
|
+
apiKey: this.config.ApiKey ?? process.env.LIVEKIT_API_KEY ?? '',
|
|
165
|
+
apiSecret: this.config.ApiSecret ?? process.env.LIVEKIT_API_SECRET ?? '',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=livekit-sip-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"livekit-sip-service.js","sourceRoot":"","sources":["../src/livekit-sip-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAA4B,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,qGAAqG;AACrG,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAE9C,sEAAsE;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,2BAA2B;IAC3B,YAAY,EAAE,iBAAiB;IAC/B,qDAAqD;IACrD,YAAY,EAAE,sBAAsB;IACpC,UAAU,EAAE,gBAAgB;IAC5B,MAAM,EAAE,YAAY;CACZ,CAAC;AAEX,yGAAyG;AACzG,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,CAAC;AAEnD,6GAA6G;AAC7G,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAwElD,gEAAgE;AAChE,MAAM,OAAO,iBAAiB;IAK5B;;;;OAIG;IACH,YAAY,MAAqC,EAAE,GAAmB,EAAE,KAA6B;QACnG,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,0EAA0E;IAC1E,IAAW,YAAY;QACrB,OAAO,IAAI,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC;IAC3D,CAAC;IAED,gFAAgF;IAEhF,qGAAqG;IAC9F,KAAK,CAAC,gBAAgB,CAAC,QAAgB;QAC5C,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACxE,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9B,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,IAAI,KAAK,4BAA4B;YAC9C,WAAW,EAAE,CAAC,CAAC,KAAK,KAAK,qBAAqB,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,qBAAqB,CAAC,MAAM;YACjG,UAAU,EAAE,EAAE,GAAG,CAAC,CAAC,UAAU,EAAE;SAChC,CAAC,CAAC,CAAC;IACN,CAAC;IAED,8FAA8F;IACvF,KAAK,CAAC,oBAAoB,CAAC,QAAgB,EAAE,QAAgB;QAClE,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC3D,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;IACxF,CAAC;IAED,gEAAgE;IACzD,KAAK,CAAC,iBAAiB,CAAC,QAAgB,EAAE,QAAgB;QAC/D,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChE,CAAC;IAED,gFAAgF;IAEhF;;;OAGG;IACI,KAAK,CAAC,YAAY,CAAC,MAAgC;QACxD,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;YAC1F,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;YAC/C,eAAe,EAAE,MAAM,CAAC,WAAW;YACnC,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,cAAc,EAAE,MAAM,CAAC,kBAAkB;YACzC,eAAe,EAAE,6BAA6B;YAC9C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,IAAI;YACnD,OAAO,EAAE,gCAAgC;SAC1C,CAAC,CAAC;IACL,CAAC;IAED,gFAAgF;IAEhF,6FAA6F;IACtF,KAAK,CAAC,iBAAiB,CAAC,OAAkB;QAC/C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACrG,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/F,CAAC;IAED,uCAAuC;IAChC,KAAK,CAAC,kBAAkB;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,oBAAoB,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/F,CAAC;IAED,uEAAuE;IAChE,KAAK,CAAC,mBAAmB,CAAC,OAAe;QAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC/C,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IACnD,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,oBAAoB,CAAC,MAAkC;QAClE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,wBAAwB,CAAC;QACrD,MAAM,aAAa,GAAG,CAAC,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrI,MAAM,KAAK,GAAG,aAAa,IAAI,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC3E,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,aAAa,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;QACpH,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,qBAAqB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,WAAW,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7K,SAAS,CAAC,sCAAsC,OAAO,CAAC,iBAAiB,YAAY,MAAM,CAAC,UAAU,gBAAgB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;QACxI,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,iBAAiB,EAAE,YAAY,EAAE,CAAC,aAAa,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC;IACxI,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,IAAY,EAAE,MAAkC;QAC/E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE;YACjF,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;YACvF,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,YAAY,EAAE,MAAM,CAAC,YAAY;SAClC,CAAC,CAAC;QACH,SAAS,CAAC,sCAAsC,OAAO,CAAC,UAAU,QAAQ,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,CAAC,CAAC;QAC9G,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5F,CAAC;IAED,uGAAuG;IAC/F,KAAK,CAAC,gBAAgB,CAAC,OAAe,EAAE,UAAkB;QAChE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,mBAAmB,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,wBAAwB,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;QAC/H,OAAO,KAAK,EAAE,iBAAiB,CAAC;IAClC,CAAC;IAED,gFAAgF;IAExE,SAAS;QACf,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACvD,IAAI,CAAC,GAAG,GAAG,IAAI,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACvD,IAAI,CAAC,KAAK,GAAG,IAAI,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,2FAA2F;IACnF,WAAW;QACjB,MAAM,MAAM,GAAG,IAAI,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,qFAAqF,CAAC,CAAC;QACzG,CAAC;QACD,OAAO;YACL,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC;YACnC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE;YAC/D,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE;SACzE,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -52,6 +52,12 @@ export interface MintedToken {
|
|
|
52
52
|
/** The room name the token authorizes. */
|
|
53
53
|
RoomName: string;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* The LiveKit participant identity a signed-in MJ user joins a room with. Everything that mints a token for a user, and
|
|
57
|
+
* everything that later looks for that user in a room (the handoff engine watching for a person to appear), derives it
|
|
58
|
+
* here so the two can never disagree.
|
|
59
|
+
*/
|
|
60
|
+
export declare function LiveKitUserIdentity(userID: string): string;
|
|
55
61
|
/** Mints scoped LiveKit access tokens. Stateless apart from its resolved credentials. */
|
|
56
62
|
export declare class LiveKitTokenService {
|
|
57
63
|
private readonly config;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"livekit-token-service.d.ts","sourceRoot":"","sources":["../src/livekit-token-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,gGAAgG;AAChG,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC;AAEhE,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sEAAsE;IACtE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,yFAAyF;AACzF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAE7C;;;OAGG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC;IAQjD,qEAAqE;IACrE,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED,iFAAiF;IACjF,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED;;;;;;OAMG;IACU,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAarE;;;;;;;OAOG;IACI,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAI3H;;;;;;;OAOG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAInG,oGAAoG;IACpG,OAAO,CAAC,UAAU;IAUlB,+FAA+F;IAC/F,OAAO,CAAC,aAAa;IAIrB,yDAAyD;IACzD,OAAO,CAAC,gBAAgB;CAQzB"}
|
|
1
|
+
{"version":3,"file":"livekit-token-service.d.ts","sourceRoot":"","sources":["../src/livekit-token-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,gGAAgG;AAChG,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC;AAEhE,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sEAAsE;IACtE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,yFAAyF;AACzF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAE7C;;;OAGG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC;IAQjD,qEAAqE;IACrE,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED,iFAAiF;IACjF,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED;;;;;;OAMG;IACU,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAarE;;;;;;;OAOG;IACI,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAI3H;;;;;;;OAOG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAInG,oGAAoG;IACpG,OAAO,CAAC,UAAU;IAUlB,+FAA+F;IAC/F,OAAO,CAAC,aAAa;IAIrB,yDAAyD;IACzD,OAAO,CAAC,gBAAgB;CAQzB"}
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
* @module @memberjunction/livekit-room-server
|
|
11
11
|
*/
|
|
12
12
|
import { AccessToken } from 'livekit-server-sdk';
|
|
13
|
+
/**
|
|
14
|
+
* The LiveKit participant identity a signed-in MJ user joins a room with. Everything that mints a token for a user, and
|
|
15
|
+
* everything that later looks for that user in a room (the handoff engine watching for a person to appear), derives it
|
|
16
|
+
* here so the two can never disagree.
|
|
17
|
+
*/
|
|
18
|
+
export function LiveKitUserIdentity(userID) {
|
|
19
|
+
return `user-${userID}`.toLowerCase();
|
|
20
|
+
}
|
|
13
21
|
/** Mints scoped LiveKit access tokens. Stateless apart from its resolved credentials. */
|
|
14
22
|
export class LiveKitTokenService {
|
|
15
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"livekit-token-service.js","sourceRoot":"","sources":["../src/livekit-token-service.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"livekit-token-service.js","sourceRoot":"","sources":["../src/livekit-token-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview {@link LiveKitWebhookParser}: verifies and reads LiveKit's server webhooks.
|
|
3
|
+
*
|
|
4
|
+
* LiveKit posts room and participant events (`room_started`, `participant_joined`, `participant_left`, …) to a URL
|
|
5
|
+
* configured on the project, signed with the project's API secret. For phone calls the event MJ acts on is a **SIP
|
|
6
|
+
* participant joining a room**: it is how MJ learns that a call arrived and which number was dialed. This parser checks
|
|
7
|
+
* the signature (a forged event must never make MJ start an agent) and reduces the SDK's protobuf event to a plain value.
|
|
8
|
+
*
|
|
9
|
+
* @module @memberjunction/livekit-room-server
|
|
10
|
+
*/
|
|
11
|
+
import { type LiveKitServerConfig } from './livekit-token-service.js';
|
|
12
|
+
/** A LiveKit webhook event reduced to what MJ looks at. */
|
|
13
|
+
export interface LiveKitRoomWebhookEvent {
|
|
14
|
+
/** The event name, e.g. `participant_joined`. */
|
|
15
|
+
Event: string;
|
|
16
|
+
RoomName: string;
|
|
17
|
+
ParticipantIdentity: string;
|
|
18
|
+
/** True when the participant is a phone (SIP) participant. */
|
|
19
|
+
IsSipParticipant: boolean;
|
|
20
|
+
/** The caller's number, for a SIP participant (`sip.phoneNumber`). */
|
|
21
|
+
CallerNumber?: string;
|
|
22
|
+
/** The number that was dialed, for a SIP participant (`sip.trunkPhoneNumber`). */
|
|
23
|
+
DialedNumber?: string;
|
|
24
|
+
/** LiveKit's call id, for a SIP participant. */
|
|
25
|
+
SipCallID?: string;
|
|
26
|
+
}
|
|
27
|
+
/** The part of the SDK receiver the parser uses (so tests inject a fake). */
|
|
28
|
+
export interface WebhookReceiverLike {
|
|
29
|
+
receive(body: string, authHeader?: string): Promise<{
|
|
30
|
+
event: string;
|
|
31
|
+
room?: {
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
34
|
+
participant?: {
|
|
35
|
+
identity: string;
|
|
36
|
+
kind: number;
|
|
37
|
+
attributes: {
|
|
38
|
+
[key: string]: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/** Verifies and parses LiveKit webhooks. */
|
|
44
|
+
export declare class LiveKitWebhookParser {
|
|
45
|
+
private readonly receiver;
|
|
46
|
+
/**
|
|
47
|
+
* @param config Explicit credentials; omitted fields fall back to the `LIVEKIT_*` environment variables.
|
|
48
|
+
* @param receiver Injectable receiver (tests); defaults to the SDK's `WebhookReceiver`.
|
|
49
|
+
* @throws {Error} when no receiver is injected and the API key and secret are not configured.
|
|
50
|
+
*/
|
|
51
|
+
constructor(config?: Partial<LiveKitServerConfig>, receiver?: WebhookReceiverLike);
|
|
52
|
+
/**
|
|
53
|
+
* Verifies the request's signature and returns the event.
|
|
54
|
+
*
|
|
55
|
+
* One event is also acted on here, because every LiveKit webhook MJ receives passes through this method: on a
|
|
56
|
+
* verified `room_finished` the room's {@link RoomAudioPlayer} playbacks (hold music) are stopped, so a bot never
|
|
57
|
+
* outlives its room. That cleanup runs in the background and never throws.
|
|
58
|
+
*
|
|
59
|
+
* @param body The raw request body (the signature covers the exact bytes, so do not re-serialise parsed JSON).
|
|
60
|
+
* @param authHeader The request's `Authorization` header.
|
|
61
|
+
* @throws {Error} when the signature is missing or does not verify.
|
|
62
|
+
*/
|
|
63
|
+
Parse(body: string, authHeader?: string): Promise<LiveKitRoomWebhookEvent>;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=livekit-webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"livekit-webhook.d.ts","sourceRoot":"","sources":["../src/livekit-webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAIxF,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAClD,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QACxB,WAAW,CAAC,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE;gBAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;aAAE,CAAA;SAAE,CAAC;KACzF,CAAC,CAAC;CACJ;AAED,4CAA4C;AAC5C,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAE/C;;;;OAIG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,EAAE,QAAQ,CAAC,EAAE,mBAAmB;IAajF;;;;;;;;;;OAUG;IACU,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;CAmBxF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview {@link LiveKitWebhookParser}: verifies and reads LiveKit's server webhooks.
|
|
3
|
+
*
|
|
4
|
+
* LiveKit posts room and participant events (`room_started`, `participant_joined`, `participant_left`, …) to a URL
|
|
5
|
+
* configured on the project, signed with the project's API secret. For phone calls the event MJ acts on is a **SIP
|
|
6
|
+
* participant joining a room**: it is how MJ learns that a call arrived and which number was dialed. This parser checks
|
|
7
|
+
* the signature (a forged event must never make MJ start an agent) and reduces the SDK's protobuf event to a plain value.
|
|
8
|
+
*
|
|
9
|
+
* @module @memberjunction/livekit-room-server
|
|
10
|
+
*/
|
|
11
|
+
import { WebhookReceiver } from 'livekit-server-sdk';
|
|
12
|
+
import { LiveKitTokenService } from './livekit-token-service.js';
|
|
13
|
+
import { LIVEKIT_PARTICIPANT_KIND_SIP, LIVEKIT_SIP_ATTRIBUTES } from './livekit-sip-service.js';
|
|
14
|
+
import { RoomAudioPlayer } from './room-audio/room-audio-player.js';
|
|
15
|
+
/** Verifies and parses LiveKit webhooks. */
|
|
16
|
+
export class LiveKitWebhookParser {
|
|
17
|
+
/**
|
|
18
|
+
* @param config Explicit credentials; omitted fields fall back to the `LIVEKIT_*` environment variables.
|
|
19
|
+
* @param receiver Injectable receiver (tests); defaults to the SDK's `WebhookReceiver`.
|
|
20
|
+
* @throws {Error} when no receiver is injected and the API key and secret are not configured.
|
|
21
|
+
*/
|
|
22
|
+
constructor(config, receiver) {
|
|
23
|
+
if (receiver) {
|
|
24
|
+
this.receiver = receiver;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const apiKey = config?.ApiKey ?? process.env.LIVEKIT_API_KEY ?? '';
|
|
28
|
+
const apiSecret = config?.ApiSecret ?? process.env.LIVEKIT_API_SECRET ?? '';
|
|
29
|
+
if (!new LiveKitTokenService(config).IsConfigured) {
|
|
30
|
+
throw new Error('LiveKit webhooks cannot be verified: LIVEKIT_URL, LIVEKIT_API_KEY and LIVEKIT_API_SECRET are not all set.');
|
|
31
|
+
}
|
|
32
|
+
this.receiver = new WebhookReceiver(apiKey, apiSecret);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Verifies the request's signature and returns the event.
|
|
36
|
+
*
|
|
37
|
+
* One event is also acted on here, because every LiveKit webhook MJ receives passes through this method: on a
|
|
38
|
+
* verified `room_finished` the room's {@link RoomAudioPlayer} playbacks (hold music) are stopped, so a bot never
|
|
39
|
+
* outlives its room. That cleanup runs in the background and never throws.
|
|
40
|
+
*
|
|
41
|
+
* @param body The raw request body (the signature covers the exact bytes, so do not re-serialise parsed JSON).
|
|
42
|
+
* @param authHeader The request's `Authorization` header.
|
|
43
|
+
* @throws {Error} when the signature is missing or does not verify.
|
|
44
|
+
*/
|
|
45
|
+
async Parse(body, authHeader) {
|
|
46
|
+
const event = await this.receiver.receive(body, authHeader);
|
|
47
|
+
const participant = event.participant;
|
|
48
|
+
const isSip = participant?.kind === LIVEKIT_PARTICIPANT_KIND_SIP;
|
|
49
|
+
const attributes = participant?.attributes ?? {};
|
|
50
|
+
const roomName = event.room?.name ?? '';
|
|
51
|
+
if (event.event === 'room_finished' && roomName) {
|
|
52
|
+
void RoomAudioPlayer.Instance.StopAllInRoom(roomName);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
Event: event.event,
|
|
56
|
+
RoomName: roomName,
|
|
57
|
+
ParticipantIdentity: participant?.identity ?? '',
|
|
58
|
+
IsSipParticipant: isSip,
|
|
59
|
+
CallerNumber: isSip ? attributes[LIVEKIT_SIP_ATTRIBUTES.CallerNumber] : undefined,
|
|
60
|
+
DialedNumber: isSip ? attributes[LIVEKIT_SIP_ATTRIBUTES.DialedNumber] : undefined,
|
|
61
|
+
SipCallID: isSip ? attributes[LIVEKIT_SIP_ATTRIBUTES.CallID] : undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=livekit-webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"livekit-webhook.js","sourceRoot":"","sources":["../src/livekit-webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAA4B,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AA2BjE,4CAA4C;AAC5C,MAAM,OAAO,oBAAoB;IAG/B;;;;OAIG;IACH,YAAY,MAAqC,EAAE,QAA8B;QAC/E,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACzB,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;QACnE,MAAM,SAAS,GAAG,MAAM,EAAE,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC;QAC5E,IAAI,CAAC,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,2GAA2G,CAAC,CAAC;QAC/H,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzD,CAAC;IAED;;;;;;;;;;OAUG;IACI,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,UAAmB;QAClD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC5D,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;QACtC,MAAM,KAAK,GAAG,WAAW,EAAE,IAAI,KAAK,4BAA4B,CAAC;QACjE,MAAM,UAAU,GAAG,WAAW,EAAE,UAAU,IAAI,EAAE,CAAC;QACjD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;QACxC,IAAI,KAAK,CAAC,KAAK,KAAK,eAAe,IAAI,QAAQ,EAAE,CAAC;YAChD,KAAK,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxD,CAAC;QACD,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,QAAQ;YAClB,mBAAmB,EAAE,WAAW,EAAE,QAAQ,IAAI,EAAE;YAChD,gBAAgB,EAAE,KAAK;YACvB,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;YACjF,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;YACjF,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;SACzE,CAAC;IACJ,CAAC;CACF"}
|