@memberjunction/server 6.1.0-edge.5 → 6.1.0-edge.7

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 (250) hide show
  1. package/README.md +30 -11
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.js +2 -2
  4. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  5. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  6. package/dist/auth/exampleNewUserSubClass.js +9 -4
  7. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  8. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  9. package/dist/auth/magicLink/MagicLinkService.js +2 -8
  10. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  11. package/dist/auth/newUsers.d.ts.map +1 -1
  12. package/dist/auth/newUsers.js +4 -12
  13. package/dist/auth/newUsers.js.map +1 -1
  14. package/dist/auth/principals.d.ts +92 -0
  15. package/dist/auth/principals.d.ts.map +1 -0
  16. package/dist/auth/principals.js +193 -0
  17. package/dist/auth/principals.js.map +1 -0
  18. package/dist/config.d.ts +106 -12
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +41 -4
  21. package/dist/config.js.map +1 -1
  22. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts +30 -0
  23. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts.map +1 -1
  24. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js +58 -5
  25. package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js.map +1 -1
  26. package/dist/generated/graphql-schemas/__mj.d.ts +7607 -6853
  27. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  28. package/dist/generated/graphql-schemas/__mj.js +25141 -21645
  29. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  30. package/dist/generic/ResolverBase.d.ts +152 -2
  31. package/dist/generic/ResolverBase.d.ts.map +1 -1
  32. package/dist/generic/ResolverBase.js +389 -10
  33. package/dist/generic/ResolverBase.js.map +1 -1
  34. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  35. package/dist/generic/RunViewResolver.js +26 -2
  36. package/dist/generic/RunViewResolver.js.map +1 -1
  37. package/dist/generic/refusalExtensions.d.ts +52 -0
  38. package/dist/generic/refusalExtensions.d.ts.map +1 -0
  39. package/dist/generic/refusalExtensions.js +16 -0
  40. package/dist/generic/refusalExtensions.js.map +1 -0
  41. package/dist/index.d.ts +11 -3
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +131 -93
  44. package/dist/index.js.map +1 -1
  45. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  46. package/dist/realtimeWidget/WidgetSessionService.js +2 -12
  47. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  48. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  49. package/dist/realtimeWidget/visitorIdentity.js +20 -5
  50. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  51. package/dist/resolvers/EntityRecordNameResolver.d.ts +34 -5
  52. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  53. package/dist/resolvers/EntityRecordNameResolver.js +64 -7
  54. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  55. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  56. package/dist/resolvers/FileCategoryResolver.js +1 -2
  57. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  58. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  59. package/dist/resolvers/IntegrationDiscoveryResolver.js +2 -0
  60. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  61. package/dist/resolvers/QuerySystemUserResolver.js +1 -1
  62. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  63. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -2
  64. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  65. package/dist/resolvers/RealtimeClientSessionResolver.js +80 -4
  66. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  67. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +13 -8
  68. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  69. package/dist/resolvers/RemoteBrowserActionResolver.js +45 -8
  70. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  71. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  72. package/dist/resolvers/SyncRolesUsersResolver.js +11 -2
  73. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  74. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  75. package/dist/resolvers/UserViewResolver.js +8 -4
  76. package/dist/resolvers/UserViewResolver.js.map +1 -1
  77. package/dist/rest/EntityCRUDHandler.d.ts +2 -1
  78. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  79. package/dist/rest/EntityCRUDHandler.js +3 -18
  80. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  81. package/dist/rest/RESTEndpointHandler.d.ts +2 -1
  82. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  83. package/dist/rest/RESTEndpointHandler.js +3 -16
  84. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  85. package/dist/rest/RealtimeSdpBrokerHandler.d.ts +23 -0
  86. package/dist/rest/RealtimeSdpBrokerHandler.d.ts.map +1 -0
  87. package/dist/rest/RealtimeSdpBrokerHandler.js +78 -0
  88. package/dist/rest/RealtimeSdpBrokerHandler.js.map +1 -0
  89. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  90. package/dist/serverExtensionReservedRoots.js +2 -3
  91. package/dist/serverExtensionReservedRoots.js.map +1 -1
  92. package/package.json +92 -91
  93. package/src/__tests__/EntityCRUDHandler.test.ts +18 -5
  94. package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +118 -0
  95. package/src/__tests__/RealtimeClientSessionResolver.test.ts +20 -0
  96. package/src/__tests__/RealtimeSdpBrokerHandler.test.ts +191 -0
  97. package/src/__tests__/RemoteBrowserAudioStream.test.ts +81 -2
  98. package/src/__tests__/ResolverBase.filterEscaping.test.ts +190 -3
  99. package/src/__tests__/ResolverBase.readableFieldsTransport.test.ts +106 -0
  100. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +114 -0
  101. package/src/__tests__/WidgetSessionService.test.ts +4 -1
  102. package/src/__tests__/generatedGraphQLSchema.structural.test.ts +46 -0
  103. package/src/__tests__/newUserRolesDefault.test.ts +81 -0
  104. package/src/__tests__/newUsers.test.ts +36 -14
  105. package/src/__tests__/principals.callSites.test.ts +112 -0
  106. package/src/__tests__/principals.logging.test.ts +160 -0
  107. package/src/__tests__/principals.test.ts +305 -0
  108. package/src/__tests__/resolverBase.fls.test.ts +483 -0
  109. package/src/__tests__/resolverBase.recordChangesFls.test.ts +358 -0
  110. package/src/__tests__/resolverBase.refusalExtensions.test.ts +101 -0
  111. package/src/__tests__/resolverBase.refusalMessage.test.ts +88 -0
  112. package/src/__tests__/serverExtensionReservedRoots.test.ts +79 -11
  113. package/src/__tests__/visitorIdentity.resolve.test.ts +61 -0
  114. package/src/agentSessions/SessionJanitor.ts +2 -2
  115. package/src/auth/exampleNewUserSubClass.ts +9 -6
  116. package/src/auth/magicLink/MagicLinkService.ts +2 -8
  117. package/src/auth/newUsers.ts +7 -15
  118. package/src/auth/principals.ts +257 -0
  119. package/src/config.ts +44 -4
  120. package/src/entitySubclasses/MJEntityPermissionEntityServer.server.ts +65 -6
  121. package/src/generated/graphql-schemas/__mj.ts +18966 -16452
  122. package/src/generic/ResolverBase.ts +467 -9
  123. package/src/generic/RunViewResolver.ts +28 -2
  124. package/src/generic/refusalExtensions.ts +59 -0
  125. package/src/index.ts +154 -102
  126. package/src/realtimeWidget/WidgetSessionService.ts +2 -14
  127. package/src/realtimeWidget/visitorIdentity.ts +21 -6
  128. package/src/resolvers/EntityRecordNameResolver.ts +74 -8
  129. package/src/resolvers/FileCategoryResolver.ts +2 -3
  130. package/src/resolvers/IntegrationDiscoveryResolver.ts +2 -0
  131. package/src/resolvers/QuerySystemUserResolver.ts +1 -1
  132. package/src/resolvers/RealtimeClientSessionResolver.ts +128 -2
  133. package/src/resolvers/RemoteBrowserActionResolver.ts +47 -8
  134. package/src/resolvers/SyncRolesUsersResolver.ts +11 -2
  135. package/src/resolvers/UserViewResolver.ts +8 -4
  136. package/src/rest/EntityCRUDHandler.ts +3 -19
  137. package/src/rest/RESTEndpointHandler.ts +3 -17
  138. package/src/rest/RealtimeSdpBrokerHandler.ts +101 -0
  139. package/src/serverExtensionReservedRoots.ts +2 -3
  140. package/dist/resolvers/RingCentralTelephonyResolver.d.ts +0 -27
  141. package/dist/resolvers/RingCentralTelephonyResolver.d.ts.map +0 -1
  142. package/dist/resolvers/RingCentralTelephonyResolver.js +0 -87
  143. package/dist/resolvers/RingCentralTelephonyResolver.js.map +0 -1
  144. package/dist/resolvers/TeamsMeetingsResolver.d.ts +0 -28
  145. package/dist/resolvers/TeamsMeetingsResolver.d.ts.map +0 -1
  146. package/dist/resolvers/TeamsMeetingsResolver.js +0 -91
  147. package/dist/resolvers/TeamsMeetingsResolver.js.map +0 -1
  148. package/dist/resolvers/TelephonyResolver.d.ts +0 -26
  149. package/dist/resolvers/TelephonyResolver.d.ts.map +0 -1
  150. package/dist/resolvers/TelephonyResolver.js +0 -86
  151. package/dist/resolvers/TelephonyResolver.js.map +0 -1
  152. package/dist/resolvers/VonageTelephonyResolver.d.ts +0 -26
  153. package/dist/resolvers/VonageTelephonyResolver.d.ts.map +0 -1
  154. package/dist/resolvers/VonageTelephonyResolver.js +0 -86
  155. package/dist/resolvers/VonageTelephonyResolver.js.map +0 -1
  156. package/dist/telephony/RingCentralTelephonyService.d.ts +0 -115
  157. package/dist/telephony/RingCentralTelephonyService.d.ts.map +0 -1
  158. package/dist/telephony/RingCentralTelephonyService.js +0 -262
  159. package/dist/telephony/RingCentralTelephonyService.js.map +0 -1
  160. package/dist/telephony/TeamsMeetingsRouter.d.ts +0 -40
  161. package/dist/telephony/TeamsMeetingsRouter.d.ts.map +0 -1
  162. package/dist/telephony/TeamsMeetingsRouter.js +0 -96
  163. package/dist/telephony/TeamsMeetingsRouter.js.map +0 -1
  164. package/dist/telephony/TeamsMeetingsService.d.ts +0 -113
  165. package/dist/telephony/TeamsMeetingsService.d.ts.map +0 -1
  166. package/dist/telephony/TeamsMeetingsService.js +0 -196
  167. package/dist/telephony/TeamsMeetingsService.js.map +0 -1
  168. package/dist/telephony/TwilioTelephonyRouter.d.ts +0 -36
  169. package/dist/telephony/TwilioTelephonyRouter.d.ts.map +0 -1
  170. package/dist/telephony/TwilioTelephonyRouter.js +0 -143
  171. package/dist/telephony/TwilioTelephonyRouter.js.map +0 -1
  172. package/dist/telephony/TwilioTelephonyService.d.ts +0 -95
  173. package/dist/telephony/TwilioTelephonyService.d.ts.map +0 -1
  174. package/dist/telephony/TwilioTelephonyService.js +0 -193
  175. package/dist/telephony/TwilioTelephonyService.js.map +0 -1
  176. package/dist/telephony/VonageTelephonyRouter.d.ts +0 -41
  177. package/dist/telephony/VonageTelephonyRouter.d.ts.map +0 -1
  178. package/dist/telephony/VonageTelephonyRouter.js +0 -201
  179. package/dist/telephony/VonageTelephonyRouter.js.map +0 -1
  180. package/dist/telephony/VonageTelephonyService.d.ts +0 -90
  181. package/dist/telephony/VonageTelephonyService.d.ts.map +0 -1
  182. package/dist/telephony/VonageTelephonyService.js +0 -191
  183. package/dist/telephony/VonageTelephonyService.js.map +0 -1
  184. package/dist/telephony/calendar-scheduler.d.ts +0 -67
  185. package/dist/telephony/calendar-scheduler.d.ts.map +0 -1
  186. package/dist/telephony/calendar-scheduler.js +0 -133
  187. package/dist/telephony/calendar-scheduler.js.map +0 -1
  188. package/dist/telephony/index.d.ts +0 -29
  189. package/dist/telephony/index.d.ts.map +0 -1
  190. package/dist/telephony/index.js +0 -38
  191. package/dist/telephony/index.js.map +0 -1
  192. package/dist/telephony/media-upgrade-router.d.ts +0 -47
  193. package/dist/telephony/media-upgrade-router.d.ts.map +0 -1
  194. package/dist/telephony/media-upgrade-router.js +0 -72
  195. package/dist/telephony/media-upgrade-router.js.map +0 -1
  196. package/dist/telephony/ringcentral-runtime.d.ts +0 -14
  197. package/dist/telephony/ringcentral-runtime.d.ts.map +0 -1
  198. package/dist/telephony/ringcentral-runtime.js +0 -18
  199. package/dist/telephony/ringcentral-runtime.js.map +0 -1
  200. package/dist/telephony/teams-meetings-runtime.d.ts +0 -16
  201. package/dist/telephony/teams-meetings-runtime.d.ts.map +0 -1
  202. package/dist/telephony/teams-meetings-runtime.js +0 -20
  203. package/dist/telephony/teams-meetings-runtime.js.map +0 -1
  204. package/dist/telephony/teamsAcsMediaRegistry.d.ts +0 -69
  205. package/dist/telephony/teamsAcsMediaRegistry.d.ts.map +0 -1
  206. package/dist/telephony/teamsAcsMediaRegistry.js +0 -118
  207. package/dist/telephony/teamsAcsMediaRegistry.js.map +0 -1
  208. package/dist/telephony/telephony-runtime.d.ts +0 -14
  209. package/dist/telephony/telephony-runtime.d.ts.map +0 -1
  210. package/dist/telephony/telephony-runtime.js +0 -18
  211. package/dist/telephony/telephony-runtime.js.map +0 -1
  212. package/dist/telephony/twilioMediaRegistry.d.ts +0 -60
  213. package/dist/telephony/twilioMediaRegistry.d.ts.map +0 -1
  214. package/dist/telephony/twilioMediaRegistry.js +0 -106
  215. package/dist/telephony/twilioMediaRegistry.js.map +0 -1
  216. package/dist/telephony/vonage-runtime.d.ts +0 -14
  217. package/dist/telephony/vonage-runtime.d.ts.map +0 -1
  218. package/dist/telephony/vonage-runtime.js +0 -18
  219. package/dist/telephony/vonage-runtime.js.map +0 -1
  220. package/dist/telephony/vonageMediaRegistry.d.ts +0 -78
  221. package/dist/telephony/vonageMediaRegistry.d.ts.map +0 -1
  222. package/dist/telephony/vonageMediaRegistry.js +0 -158
  223. package/dist/telephony/vonageMediaRegistry.js.map +0 -1
  224. package/src/__tests__/media-upgrade-router.test.ts +0 -43
  225. package/src/__tests__/teams-meetings-service.test.ts +0 -65
  226. package/src/__tests__/teamsAcsMediaRegistry.test.ts +0 -82
  227. package/src/__tests__/twilio-telephony-service.test.ts +0 -23
  228. package/src/__tests__/twilioMediaRegistry.test.ts +0 -103
  229. package/src/__tests__/vonageMediaRegistry.test.ts +0 -180
  230. package/src/resolvers/RingCentralTelephonyResolver.ts +0 -64
  231. package/src/resolvers/TeamsMeetingsResolver.ts +0 -68
  232. package/src/resolvers/TelephonyResolver.ts +0 -63
  233. package/src/resolvers/VonageTelephonyResolver.ts +0 -63
  234. package/src/telephony/RingCentralTelephonyService.ts +0 -342
  235. package/src/telephony/TeamsMeetingsRouter.ts +0 -124
  236. package/src/telephony/TeamsMeetingsService.ts +0 -268
  237. package/src/telephony/TwilioTelephonyRouter.ts +0 -184
  238. package/src/telephony/TwilioTelephonyService.ts +0 -261
  239. package/src/telephony/VonageTelephonyRouter.ts +0 -239
  240. package/src/telephony/VonageTelephonyService.ts +0 -259
  241. package/src/telephony/calendar-scheduler.ts +0 -176
  242. package/src/telephony/index.ts +0 -43
  243. package/src/telephony/media-upgrade-router.ts +0 -79
  244. package/src/telephony/ringcentral-runtime.ts +0 -22
  245. package/src/telephony/teams-meetings-runtime.ts +0 -24
  246. package/src/telephony/teamsAcsMediaRegistry.ts +0 -152
  247. package/src/telephony/telephony-runtime.ts +0 -22
  248. package/src/telephony/twilioMediaRegistry.ts +0 -137
  249. package/src/telephony/vonage-runtime.ts +0 -22
  250. package/src/telephony/vonageMediaRegistry.ts +0 -200
@@ -1,191 +0,0 @@
1
- /**
2
- * @fileoverview Orchestrates Vonage telephony bridge sessions (inbound + outbound) for MJAPI.
3
- *
4
- * This is the composition layer between the carrier ingress (the public answer/event webhooks + the
5
- * bidirectional media WSS) and the ONE unified realtime-agent pathway. It does NOT re-implement any
6
- * bridge logic: it resolves the Vonage provider + the agent identity, opens a server-side realtime
7
- * session via the agent layer's `CreateBridgeRealtimeSession` factory, binds `RealVonageBindings` (real
8
- * REST Voice client + the per-call media registry) onto the driver, and hands everything to
9
- * `AIBridgeEngine.StartBridgeSession` — exactly the seam the Twilio service + LiveKit room coordinator use.
10
- *
11
- * Collaborators are injected (engine, session factory, session manager) so the resolution/branching
12
- * logic is unit-testable with fakes; the actual call placement + media flow are live-only (real Vonage
13
- * credentials + a publicly reachable media URL — see telephony-vendor-bindings.md §T2).
14
- *
15
- * @module @memberjunction/server/telephony
16
- */
17
- import { RunView, LogError } from '@memberjunction/core';
18
- import { AIBridgeEngine } from '@memberjunction/ai-bridge-server';
19
- import { CreateBridgeRealtimeSession } from '@memberjunction/ai-agents';
20
- import { DIRECTION_CONFIG_KEY, FROM_NUMBER_CONFIG_KEY, INBOUND_CALL_ID_CONFIG_KEY, } from '@memberjunction/ai-bridge-base';
21
- import { VonageCallSdk, RealVonageBindings, RealVonageVoiceClient } from '@memberjunction/ai-bridge-vonage';
22
- import { SessionManager } from '../agentSessions/SessionManager.js';
23
- const VONAGE_PROVIDER_DRIVER = 'VonageBridge';
24
- const AGENT_IDENTITY_ENTITY = 'MJ: AI Bridge Agent Identities';
25
- const PHONE_IDENTITY_TYPE = 'PhoneNumber';
26
- /**
27
- * Starts Vonage bridge sessions. One instance per server, constructed alongside the telephony router and
28
- * sharing the {@link VonageCallMediaRegistry} with the media WSS server.
29
- */
30
- export class VonageTelephonyService {
31
- constructor(config, registry, deps = {}) {
32
- this.config = config;
33
- this.registry = registry;
34
- this.voice = new RealVonageVoiceClient({
35
- ApplicationId: config.applicationId,
36
- PrivateKey: config.privateKey,
37
- ApiKey: config.apiKey,
38
- ApiSecret: config.apiSecret,
39
- });
40
- this.engine = deps.engine ?? AIBridgeEngine.Instance;
41
- this.sessionFactory = deps.sessionFactory ?? CreateBridgeRealtimeSession;
42
- this.sessionManager = deps.sessionManager ?? new SessionManager();
43
- }
44
- /**
45
- * Resolves the dialed DID to a pinned agent and starts an INBOUND bridge session. The bound
46
- * `RealVonageBindings` will accept the media socket (opened by the answer webhook's `connect` NCCO)
47
- * for the call UUID. Returns `{ accepted:false }` (never throws) when no agent identity matches the
48
- * DID, so the webhook can answer with a polite "no agent" NCCO instead of a 500.
49
- */
50
- async HandleInboundCall(input, contextUser, provider) {
51
- try {
52
- const identity = await this.resolveAgentIdentityByPhone(input.to, contextUser);
53
- if (!identity) {
54
- return { accepted: false, reason: `No active agent identity for dialed number '${input.to}'.` };
55
- }
56
- this.registry.RegisterCall(input.callId);
57
- await this.startBridge({
58
- agentID: identity.AgentID,
59
- direction: 'Inbound',
60
- address: input.from,
61
- inboundCallId: input.callId,
62
- contextUser,
63
- provider,
64
- });
65
- return { accepted: true };
66
- }
67
- catch (e) {
68
- LogError(`[Telephony][Vonage] inbound call ${input.callId} failed: ${e instanceof Error ? e.message : String(e)}`);
69
- return { accepted: false, reason: 'Internal error starting the agent.' };
70
- }
71
- }
72
- /**
73
- * Places an OUTBOUND call from a given agent identity to a destination number. The bound
74
- * `RealVonageBindings.createCall` issues the Voice REST `POST /v1/calls` with the `connect`-websocket
75
- * NCCO; the returned call UUID is the bridge's external connection id.
76
- *
77
- * @returns The placed call UUID.
78
- */
79
- async PlaceOutboundCall(agentIdentityId, toNumber, contextUser, provider) {
80
- const identity = await this.loadAgentIdentity(agentIdentityId, contextUser, provider);
81
- if (!identity) {
82
- throw new Error(`Agent identity '${agentIdentityId}' not found or inactive.`);
83
- }
84
- const session = await this.startBridge({
85
- agentID: identity.AgentID,
86
- direction: 'Outbound',
87
- address: toNumber,
88
- fromNumber: identity.IdentityValue,
89
- contextUser,
90
- provider,
91
- });
92
- return session.RoomKey ?? '';
93
- }
94
- // ── internals ────────────────────────────────────────────────────────────────
95
- /** Shared inbound/outbound bridge-start: resolve provider, open the realtime session, bind the SDK, start. */
96
- async startBridge(args) {
97
- await this.engine.Config(false, args.contextUser, args.provider);
98
- const vonageProvider = this.resolveProvider();
99
- const agentSession = await this.sessionManager.CreateSession({ agentID: args.agentID, userID: args.contextUser.ID }, args.contextUser, args.provider);
100
- const realtimeSession = await this.sessionFactory({
101
- AgentID: args.agentID,
102
- TargetAgentID: args.agentID,
103
- ContextUser: args.contextUser,
104
- MetadataProvider: args.provider,
105
- AgentSessionID: agentSession.ID,
106
- RoomName: args.address,
107
- });
108
- const active = await this.engine.StartBridgeSession({
109
- AgentSessionID: agentSession.ID,
110
- AgentID: args.agentID,
111
- TargetAgentID: args.agentID,
112
- Provider: vonageProvider,
113
- RealtimeSession: realtimeSession,
114
- Address: args.address,
115
- Direction: args.direction,
116
- Configuration: this.buildSessionConfiguration(args.direction, args.fromNumber, args.inboundCallId),
117
- BindSdk: this.buildBindSdk(),
118
- ContextUser: args.contextUser,
119
- MetadataProvider: args.provider,
120
- });
121
- return { RoomKey: active.RoomKey };
122
- }
123
- /** Resolves the seeded Vonage provider row (by driver class, falling back to display name). */
124
- resolveProvider() {
125
- const provider = this.engine.ProviderByDriverClass(VONAGE_PROVIDER_DRIVER) ?? this.engine.ProviderByName('Vonage');
126
- if (!provider) {
127
- throw new Error(`No active 'MJ: AI Bridge Providers' row for the Vonage bridge (DriverClass '${VONAGE_PROVIDER_DRIVER}').`);
128
- }
129
- return provider;
130
- }
131
- /**
132
- * Builds the per-session SDK binding that wires the REAL Vonage bindings (REST Voice client + the
133
- * per-call media registry) onto the telephony driver — overriding the package's default unbound SDK.
134
- * This is where the offline-complete seams meet the live REST + websocket plumbing.
135
- */
136
- buildBindSdk() {
137
- return (driver) => {
138
- const telephony = driver;
139
- telephony.SetSdkFactory(() => new VonageCallSdk(new RealVonageBindings({
140
- Voice: this.voice,
141
- MediaPump: this.registry,
142
- MediaWssUrl: this.config.mediaPublicUrl,
143
- EventUrl: this.config.eventUrl,
144
- })));
145
- };
146
- }
147
- /** Assembles the per-session bridge configuration (direction/from-number/inbound-call-id config keys). */
148
- buildSessionConfiguration(direction, fromNumber, inboundCallId) {
149
- // The engine does NOT forward the top-level Direction into the driver's Configuration; the bridge
150
- // defaults to 'Outbound' when the key is absent. Set it here so an inbound call answers instead of
151
- // wrongly taking the outbound-dial path ("No 'From' number is specified").
152
- const config = { [DIRECTION_CONFIG_KEY]: direction };
153
- if (fromNumber) {
154
- config[FROM_NUMBER_CONFIG_KEY] = fromNumber;
155
- }
156
- if (inboundCallId) {
157
- config[INBOUND_CALL_ID_CONFIG_KEY] = inboundCallId;
158
- }
159
- return config;
160
- }
161
- /** Finds the active agent identity whose phone number matches the dialed DID. */
162
- async resolveAgentIdentityByPhone(dialedNumber, contextUser) {
163
- const normalized = (dialedNumber ?? '').trim();
164
- if (!normalized) {
165
- return null;
166
- }
167
- const rv = new RunView();
168
- const result = await rv.RunView({
169
- EntityName: AGENT_IDENTITY_ENTITY,
170
- ExtraFilter: `IdentityType='${PHONE_IDENTITY_TYPE}' AND IdentityValue='${normalized.replace(/'/g, "''")}' AND IsActive=1`,
171
- MaxRows: 1,
172
- ResultType: 'entity_object',
173
- }, contextUser);
174
- if (!result.Success) {
175
- LogError(`[Telephony][Vonage] agent-identity lookup failed: ${result.ErrorMessage}`);
176
- return null;
177
- }
178
- return result.Results?.[0] ?? null;
179
- }
180
- /** Loads a specific agent identity by id (used by outbound PlaceCall), requiring it be active. */
181
- async loadAgentIdentity(agentIdentityId, contextUser, provider) {
182
- // Use the request-scoped provider (multi-provider safe), not the global Metadata.
183
- const entity = await provider.GetEntityObject(AGENT_IDENTITY_ENTITY, contextUser);
184
- const loaded = await entity.Load(agentIdentityId);
185
- if (!loaded || !entity.IsActive) {
186
- return null;
187
- }
188
- return entity;
189
- }
190
- }
191
- //# sourceMappingURL=VonageTelephonyService.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"VonageTelephonyService.js","sourceRoot":"","sources":["../../src/telephony/VonageTelephonyService.ts"],"names":[],"mappings":"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"}
@@ -1,67 +0,0 @@
1
- /**
2
- * @fileoverview Host wiring for the scheduled/invite-driven meeting join loop (program M2).
3
- *
4
- * Composes the offline-complete seams from `@memberjunction/ai-bridge-server` into a running
5
- * background loop, mirroring the {@link SessionJanitor} startup pattern (run once at boot, then on an
6
- * interval):
7
- *
8
- * 1. {@link CalendarWatcher} polls each agent identity's calendar (via the injected
9
- * {@link CalendarSourceResolver}, bound here to the real {@link GraphCalendarClient} /
10
- * {@link GoogleCalendarClient}) and creates a `Scheduled` `AIAgentSessionBridge` for every invite
11
- * addressed to the agent.
12
- * 2. {@link ScheduledBridgeRunner} starts each bridge whose `ScheduledStartTime` is due, building the
13
- * realtime session + the provider bind-sdk via the per-provider meeting service (Teams today).
14
- *
15
- * Calendar credentials are PRE-RESOLVED upstream (the bot's Graph access token comes from the Teams
16
- * meetings config — `botAccessToken`, resolved via MJ config, never inline). Google calendar polling
17
- * binds only when a Google auth client is supplied; otherwise those identities are skipped (the watcher
18
- * tolerates a `null` source per identity). Live verification requires a real tenant + the Azure app /
19
- * Google service account — the same access the meeting bridges need.
20
- *
21
- * @module @memberjunction/server/telephony
22
- */
23
- import { UserInfo, IMetadataProvider } from '@memberjunction/core';
24
- import { type CalendarSourceResolver, type ScheduledBridgeSessionFactory } from '@memberjunction/ai-bridge-server';
25
- import type { TeamsMeetingsConfig } from '../config.js';
26
- import { TeamsMeetingsService } from './TeamsMeetingsService.js';
27
- /** Options for {@link StartCalendarScheduler}. */
28
- export interface CalendarSchedulerOptions {
29
- /** The server metadata provider all reads/writes run under. */
30
- Provider: IMetadataProvider;
31
- /** The system user the watcher + runner (and every created/started bridge) run as. */
32
- ContextUser: UserInfo;
33
- /** The Teams meetings service (shared with the ingress) — supplies the scheduled-join bind-sdk. */
34
- TeamsService: TeamsMeetingsService;
35
- /** The Teams meetings config — supplies the bot Graph access token for calendar polling. */
36
- TeamsConfig: TeamsMeetingsConfig;
37
- /** Poll cadence (ms). Defaults to {@link DEFAULT_POLL_INTERVAL_MS}. */
38
- PollIntervalMs?: number;
39
- }
40
- /** A running scheduler handle — call {@link CalendarSchedulerHandle.Stop} to clear the interval. */
41
- export interface CalendarSchedulerHandle {
42
- /** Stops the periodic sweep/run loop (clears the timer). Safe to call more than once. */
43
- Stop(): void;
44
- }
45
- /**
46
- * Builds the {@link CalendarSourceResolver} that binds the real calendar clients per identity. Graph
47
- * (Teams/Microsoft) identities bind a {@link GraphCalendarClient} authenticated with the bot's
48
- * pre-resolved Graph token; any identity without configured credentials resolves to `null` and is
49
- * skipped this sweep (the watcher tolerates that). Google binding activates when a Google auth client
50
- * is wired into the deployment (none configured today → Google-backed identities are skipped).
51
- */
52
- export declare function buildCalendarSourceResolver(teamsConfig: TeamsMeetingsConfig): CalendarSourceResolver;
53
- /**
54
- * Builds the {@link ScheduledBridgeSessionFactory} that turns a due bridge into start params. Resolves
55
- * the bridge's agent (via its `AIAgentSession`) and, for a Teams bridge, delegates to the shared
56
- * {@link TeamsMeetingsService} so the scheduled join reuses the exact provider/session/bind-sdk path as
57
- * an on-demand join. Returns `null` (skip) for a provider with no scheduled-join wiring yet, or when the
58
- * bridge has no join address.
59
- */
60
- export declare function buildScheduledSessionFactory(teamsService: TeamsMeetingsService): ScheduledBridgeSessionFactory;
61
- /**
62
- * Starts the calendar/scheduled-bridge loop: sweeps calendars and runs due bridges once immediately,
63
- * then on a fixed interval. Mirrors {@link SessionJanitor.Start}. A sweep/run failure is logged and the
64
- * loop continues — a transient calendar/transport error never kills the scheduler.
65
- */
66
- export declare function StartCalendarScheduler(options: CalendarSchedulerOptions): CalendarSchedulerHandle;
67
- //# sourceMappingURL=calendar-scheduler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"calendar-scheduler.d.ts","sourceRoot":"","sources":["../../src/telephony/calendar-scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAuB,MAAM,sBAAsB,CAAC;AAExF,OAAO,EAKH,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,EAErC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAQjE,kDAAkD;AAClD,MAAM,WAAW,wBAAwB;IACrC,+DAA+D;IAC/D,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,sFAAsF;IACtF,WAAW,EAAE,QAAQ,CAAC;IACtB,mGAAmG;IACnG,YAAY,EAAE,oBAAoB,CAAC;IACnC,4FAA4F;IAC5F,WAAW,EAAE,mBAAmB,CAAC;IACjC,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,oGAAoG;AACpG,MAAM,WAAW,uBAAuB;IACpC,yFAAyF;IACzF,IAAI,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,mBAAmB,GAAG,sBAAsB,CAgBpG;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,oBAAoB,GAAG,6BAA6B,CAyB9G;AAeD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,wBAAwB,GAAG,uBAAuB,CAmCjG"}
@@ -1,133 +0,0 @@
1
- /**
2
- * @fileoverview Host wiring for the scheduled/invite-driven meeting join loop (program M2).
3
- *
4
- * Composes the offline-complete seams from `@memberjunction/ai-bridge-server` into a running
5
- * background loop, mirroring the {@link SessionJanitor} startup pattern (run once at boot, then on an
6
- * interval):
7
- *
8
- * 1. {@link CalendarWatcher} polls each agent identity's calendar (via the injected
9
- * {@link CalendarSourceResolver}, bound here to the real {@link GraphCalendarClient} /
10
- * {@link GoogleCalendarClient}) and creates a `Scheduled` `AIAgentSessionBridge` for every invite
11
- * addressed to the agent.
12
- * 2. {@link ScheduledBridgeRunner} starts each bridge whose `ScheduledStartTime` is due, building the
13
- * realtime session + the provider bind-sdk via the per-provider meeting service (Teams today).
14
- *
15
- * Calendar credentials are PRE-RESOLVED upstream (the bot's Graph access token comes from the Teams
16
- * meetings config — `botAccessToken`, resolved via MJ config, never inline). Google calendar polling
17
- * binds only when a Google auth client is supplied; otherwise those identities are skipped (the watcher
18
- * tolerates a `null` source per identity). Live verification requires a real tenant + the Azure app /
19
- * Google service account — the same access the meeting bridges need.
20
- *
21
- * @module @memberjunction/server/telephony
22
- */
23
- import { LogStatus, LogError } from '@memberjunction/core';
24
- import { CalendarWatcher, ScheduledBridgeRunner, GraphCalendarSource, GraphCalendarClient, } from '@memberjunction/ai-bridge-server';
25
- /** The Teams bridge driver class — identities/bridges on this provider poll Graph + join via the Teams service. */
26
- const TEAMS_PROVIDER_DRIVER = 'TeamsBridge';
27
- /** Default cadence (ms) for the calendar sweep + due-bridge run. 60s balances freshness vs. API quota. */
28
- const DEFAULT_POLL_INTERVAL_MS = 60_000;
29
- /**
30
- * Builds the {@link CalendarSourceResolver} that binds the real calendar clients per identity. Graph
31
- * (Teams/Microsoft) identities bind a {@link GraphCalendarClient} authenticated with the bot's
32
- * pre-resolved Graph token; any identity without configured credentials resolves to `null` and is
33
- * skipped this sweep (the watcher tolerates that). Google binding activates when a Google auth client
34
- * is wired into the deployment (none configured today → Google-backed identities are skipped).
35
- */
36
- export function buildCalendarSourceResolver(teamsConfig) {
37
- return (_identity, provider) => {
38
- const driver = provider?.DriverClass?.trim();
39
- if (driver === TEAMS_PROVIDER_DRIVER && teamsConfig.botAccessToken) {
40
- return new GraphCalendarSource(new GraphCalendarClient({ Credentials: { AccessToken: teamsConfig.botAccessToken } }));
41
- }
42
- // Google Calendar is a deliberate, CLOSED seam — intentionally not bound here. The
43
- // GoogleCalendarClient is built + unit-tested, but binding it would be dead code today:
44
- // 1. there is no Google Meet *join* path for a detected invite to trigger (the scheduled-join
45
- // factory is Teams-only), and
46
- // 2. Google's Meet Media API is Developer-Preview + allowlist-gated regardless (every meeting
47
- // participant must be enrolled), so it isn't a shippable target.
48
- // To activate: define a Google auth client from config AND wire a Google Meet join. Until then
49
- // Google identities are skipped rather than erroring the sweep.
50
- return null;
51
- };
52
- }
53
- /**
54
- * Builds the {@link ScheduledBridgeSessionFactory} that turns a due bridge into start params. Resolves
55
- * the bridge's agent (via its `AIAgentSession`) and, for a Teams bridge, delegates to the shared
56
- * {@link TeamsMeetingsService} so the scheduled join reuses the exact provider/session/bind-sdk path as
57
- * an on-demand join. Returns `null` (skip) for a provider with no scheduled-join wiring yet, or when the
58
- * bridge has no join address.
59
- */
60
- export function buildScheduledSessionFactory(teamsService) {
61
- return async (ctx) => {
62
- const joinUrl = ctx.BridgeRow.Address?.trim();
63
- if (!joinUrl) {
64
- LogStatus(`[CalendarScheduler] bridge ${ctx.BridgeRow.ID} has no join address; skipping.`);
65
- return null;
66
- }
67
- if (ctx.Provider.DriverClass?.trim() !== TEAMS_PROVIDER_DRIVER) {
68
- LogStatus(`[CalendarScheduler] no scheduled-join wiring for provider '${ctx.Provider.Name}'; skipping bridge ${ctx.BridgeRow.ID}.`);
69
- return null;
70
- }
71
- const agentID = await resolveAgentID(ctx.BridgeRow.AgentSessionID, ctx.ContextUser, ctx.MetadataProvider);
72
- if (!agentID) {
73
- LogStatus(`[CalendarScheduler] could not resolve agent for session ${ctx.BridgeRow.AgentSessionID}; skipping.`);
74
- return null;
75
- }
76
- const params = await teamsService.BuildScheduledStartParams({
77
- agentID,
78
- joinUrl,
79
- agentSessionID: ctx.BridgeRow.AgentSessionID,
80
- contextUser: ctx.ContextUser,
81
- provider: ctx.MetadataProvider,
82
- });
83
- return params;
84
- };
85
- }
86
- /** Loads the `AIAgentSession` to resolve the agent the scheduled bridge belongs to. */
87
- async function resolveAgentID(agentSessionID, contextUser, provider) {
88
- const session = await provider.GetEntityObject('MJ: AI Agent Sessions', contextUser);
89
- if (!(await session.Load(agentSessionID))) {
90
- return null;
91
- }
92
- return session.AgentID;
93
- }
94
- /**
95
- * Starts the calendar/scheduled-bridge loop: sweeps calendars and runs due bridges once immediately,
96
- * then on a fixed interval. Mirrors {@link SessionJanitor.Start}. A sweep/run failure is logged and the
97
- * loop continues — a transient calendar/transport error never kills the scheduler.
98
- */
99
- export function StartCalendarScheduler(options) {
100
- const watcher = new CalendarWatcher({
101
- SourceResolver: buildCalendarSourceResolver(options.TeamsConfig),
102
- ContextUser: options.ContextUser,
103
- MetadataProvider: options.Provider,
104
- });
105
- const runner = new ScheduledBridgeRunner({
106
- SessionFactory: buildScheduledSessionFactory(options.TeamsService),
107
- ContextUser: options.ContextUser,
108
- MetadataProvider: options.Provider,
109
- });
110
- const tick = async () => {
111
- try {
112
- const swept = await watcher.Sweep();
113
- const ran = await runner.RunDueBridges();
114
- if (swept.BridgesCreated > 0 || ran.Started > 0) {
115
- LogStatus(`[CalendarScheduler] swept ${swept.BridgesCreated} new bridge(s); started ${ran.Started} due bridge(s).`);
116
- }
117
- }
118
- catch (err) {
119
- LogError(`[CalendarScheduler] sweep/run pass failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
120
- }
121
- };
122
- void tick(); // run-once at boot
123
- const timer = setInterval(() => void tick(), options.PollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
124
- // Don't let the poll timer keep the process alive at shutdown (the loop is best-effort background work).
125
- timer.unref?.();
126
- LogStatus(`[CalendarScheduler] started (every ${(options.PollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS) / 1000}s).`);
127
- return {
128
- Stop() {
129
- clearInterval(timer);
130
- },
131
- };
132
- }
133
- //# sourceMappingURL=calendar-scheduler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"calendar-scheduler.js","sourceRoot":"","sources":["../../src/telephony/calendar-scheduler.ts"],"names":[],"mappings":"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"}
@@ -1,29 +0,0 @@
1
- /**
2
- * @fileoverview Public exports for the MJAPI telephony + meetings ingress.
3
- *
4
- * Each vendor ingress is a quartet: a per-call media registry, a bridge-orchestration service, an
5
- * Express/WSS router (the `create*Handler` factory + mount-path consts), and a process-wide runtime
6
- * holder (`Set*`/`Get*`). The routers + runtimes are star-exported; the service + registry modules are
7
- * re-exported by name to avoid star-export ambiguity on their internal, per-vendor-identical helper
8
- * types (`InboundCallInput`, `InboundCallResult`, `ITelephonyMediaSocket`). Those internal types are
9
- * not part of the package's public surface — code that needs them imports from the source module.
10
- *
11
- * @module @memberjunction/server/telephony
12
- */
13
- export * from './media-upgrade-router.js';
14
- export { TwilioCallMediaRegistry } from './twilioMediaRegistry.js';
15
- export { TwilioTelephonyService, type TwilioTelephonyServiceDeps } from './TwilioTelephonyService.js';
16
- export * from './TwilioTelephonyRouter.js';
17
- export * from './telephony-runtime.js';
18
- export { VonageCallMediaRegistry } from './vonageMediaRegistry.js';
19
- export { VonageTelephonyService, type VonageTelephonyServiceDeps } from './VonageTelephonyService.js';
20
- export * from './VonageTelephonyRouter.js';
21
- export * from './vonage-runtime.js';
22
- export { RingCentralTelephonyService, type RingCentralTelephonyServiceDeps } from './RingCentralTelephonyService.js';
23
- export * from './ringcentral-runtime.js';
24
- export * from './teamsAcsMediaRegistry.js';
25
- export * from './TeamsMeetingsService.js';
26
- export * from './TeamsMeetingsRouter.js';
27
- export * from './teams-meetings-runtime.js';
28
- export * from './calendar-scheduler.js';
29
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telephony/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,cAAc,2BAA2B,CAAC;AAG1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,KAAK,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACtG,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AAGvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,KAAK,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACtG,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AAMpC,OAAO,EAAE,2BAA2B,EAAE,KAAK,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AACrH,cAAc,0BAA0B,CAAC;AAGzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAG5C,cAAc,yBAAyB,CAAC"}
@@ -1,38 +0,0 @@
1
- /**
2
- * @fileoverview Public exports for the MJAPI telephony + meetings ingress.
3
- *
4
- * Each vendor ingress is a quartet: a per-call media registry, a bridge-orchestration service, an
5
- * Express/WSS router (the `create*Handler` factory + mount-path consts), and a process-wide runtime
6
- * holder (`Set*`/`Get*`). The routers + runtimes are star-exported; the service + registry modules are
7
- * re-exported by name to avoid star-export ambiguity on their internal, per-vendor-identical helper
8
- * types (`InboundCallInput`, `InboundCallResult`, `ITelephonyMediaSocket`). Those internal types are
9
- * not part of the package's public surface — code that needs them imports from the source module.
10
- *
11
- * @module @memberjunction/server/telephony
12
- */
13
- // ── Shared WebSocket-upgrade dispatcher (routes GraphQL + all media sockets by path) ──────
14
- export * from './media-upgrade-router.js';
15
- // ── Twilio Programmable Voice + Media Streams ──────────────────────────────────────────
16
- export { TwilioCallMediaRegistry } from './twilioMediaRegistry.js';
17
- export { TwilioTelephonyService } from './TwilioTelephonyService.js';
18
- export * from './TwilioTelephonyRouter.js';
19
- export * from './telephony-runtime.js';
20
- // ── Vonage Voice + WebSocket-media ─────────────────────────────────────────────────────
21
- export { VonageCallMediaRegistry } from './vonageMediaRegistry.js';
22
- export { VonageTelephonyService } from './VonageTelephonyService.js';
23
- export * from './VonageTelephonyRouter.js';
24
- export * from './vonage-runtime.js';
25
- // ── RingCentral SIP softphone (the only RingCentral transport with bidirectional call audio) ──
26
- // No media registry / webhook router: the softphone is an outbound SIP registration that receives inbound
27
- // INVITEs directly. The service owns the registration; the runtime holder shares it with the outbound
28
- // PlaceRingCentralCall resolver.
29
- export { RingCentralTelephonyService } from './RingCentralTelephonyService.js';
30
- export * from './ringcentral-runtime.js';
31
- // ── Teams meetings (Microsoft Graph cloud-communications + ACS application-hosted media) ──
32
- export * from './teamsAcsMediaRegistry.js';
33
- export * from './TeamsMeetingsService.js';
34
- export * from './TeamsMeetingsRouter.js';
35
- export * from './teams-meetings-runtime.js';
36
- // ── Scheduled / invite-driven meeting join loop (M2: calendar watcher + due-bridge runner) ──
37
- export * from './calendar-scheduler.js';
38
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telephony/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,6FAA6F;AAC7F,cAAc,2BAA2B,CAAC;AAE1C,0FAA0F;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAmC,MAAM,6BAA6B,CAAC;AACtG,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AAEvC,0FAA0F;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAmC,MAAM,6BAA6B,CAAC;AACtG,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AAEpC,iGAAiG;AACjG,0GAA0G;AAC1G,sGAAsG;AACtG,iCAAiC;AACjC,OAAO,EAAE,2BAA2B,EAAwC,MAAM,kCAAkC,CAAC;AACrH,cAAc,0BAA0B,CAAC;AAEzC,6FAA6F;AAC7F,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAE5C,+FAA+F;AAC/F,cAAc,yBAAyB,CAAC"}
@@ -1,47 +0,0 @@
1
- /**
2
- * @fileoverview Single WebSocket-upgrade dispatcher for the shared HTTP server.
3
- *
4
- * Why this exists: in `ws` 8.x, every `new WebSocketServer({ server })` attaches its OWN `upgrade`
5
- * listener that calls `handleUpgrade` UNCONDITIONALLY, and `handleUpgrade` does `abortHandshake(400)` for
6
- * any request whose path it doesn't own. So two `{server}`-bound servers on one HTTP server (e.g. the
7
- * GraphQL subscriptions socket on `/` AND a telephony Media-Streams socket on `/telephony/.../media`)
8
- * fight on every upgrade — the non-owning server 400s the request. That's exactly what made Twilio's
9
- * `<Connect><Stream>` fail with error 31920 ("Stream WebSocket failed to connect").
10
- *
11
- * The fix: telephony/meetings media servers are created with `{ noServer: true }` (no auto-listener) and
12
- * register their path here; at boot we strip the auto-listeners and install ONE `upgrade` listener that
13
- * routes by path — GraphQL on its root path, each media socket on its own. When no media routes are
14
- * registered (telephony disabled), we leave the GraphQL server's own listener untouched (zero change).
15
- *
16
- * @module @memberjunction/server/telephony
17
- */
18
- import type { Server as HttpServer } from 'node:http';
19
- import type { WebSocketServer } from 'ws';
20
- /** Registers a media WebSocket route. Called by each vendor router's attach step. */
21
- export declare function RegisterMediaUpgradeRoute(path: string, wss: WebSocketServer): void;
22
- /**
23
- * Whether an upgrade request's pathname should be routed to the GraphQL subscriptions socket.
24
- *
25
- * `GRAPHQL_ROOT_PATH` defaults to `/` (see `config.ts` / the docker README). On the HTTP side that
26
- * default is invisible — Express's `app.use(graphqlRootPath, ...)` mounts with PREFIX matching, so a
27
- * request to `/graphql` lands on the GraphQL middleware even when the configured root is the bare `/`.
28
- * A WebSocket upgrade has no such prefix-matching fallback: it needs an exact pathname match. With no
29
- * override, a stock install's default root (`/`) never matches the shipped MJExplorer client's hardcoded
30
- * `GRAPHQL_WS_URI` (`ws://<host>/graphql`), so every subscription upgrade gets silently rejected — surfaces
31
- * client-side as "WebSocket handshake: Unexpected response code: 400". Accept the conventional `/graphql`
32
- * suffix as an alias of the bare root so a stock install works out of the box; a non-default `graphqlPath`
33
- * (e.g. `/api`) keeps requiring an exact match, unchanged.
34
- */
35
- export declare function IsGraphQLWsPath(pathname: string, graphqlPath: string): boolean;
36
- /**
37
- * Installs the single path-routing `upgrade` dispatcher. No-op when no media routes are registered, so
38
- * the GraphQL server keeps its own listener untouched. Otherwise it removes the auto-attached listeners
39
- * (GraphQL's included) and dispatches every upgrade by pathname — GraphQL on `graphqlPath`, each media
40
- * socket on its registered path; unknown paths are destroyed.
41
- *
42
- * @param httpServer the shared HTTP server.
43
- * @param graphqlWss the GraphQL subscriptions WebSocketServer (kept, re-dispatched on its path).
44
- * @param graphqlPath the GraphQL root path (the path GraphQL's socket listens on).
45
- */
46
- export declare function InstallMediaUpgradeDispatcher(httpServer: HttpServer, graphqlWss: WebSocketServer, graphqlPath: string): void;
47
- //# sourceMappingURL=media-upgrade-router.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"media-upgrade-router.d.ts","sourceRoot":"","sources":["../../src/telephony/media-upgrade-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAmB,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAK1C,qFAAqF;AACrF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,IAAI,CAElF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9E;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAqB5H"}
@@ -1,72 +0,0 @@
1
- /**
2
- * @fileoverview Single WebSocket-upgrade dispatcher for the shared HTTP server.
3
- *
4
- * Why this exists: in `ws` 8.x, every `new WebSocketServer({ server })` attaches its OWN `upgrade`
5
- * listener that calls `handleUpgrade` UNCONDITIONALLY, and `handleUpgrade` does `abortHandshake(400)` for
6
- * any request whose path it doesn't own. So two `{server}`-bound servers on one HTTP server (e.g. the
7
- * GraphQL subscriptions socket on `/` AND a telephony Media-Streams socket on `/telephony/.../media`)
8
- * fight on every upgrade — the non-owning server 400s the request. That's exactly what made Twilio's
9
- * `<Connect><Stream>` fail with error 31920 ("Stream WebSocket failed to connect").
10
- *
11
- * The fix: telephony/meetings media servers are created with `{ noServer: true }` (no auto-listener) and
12
- * register their path here; at boot we strip the auto-listeners and install ONE `upgrade` listener that
13
- * routes by path — GraphQL on its root path, each media socket on its own. When no media routes are
14
- * registered (telephony disabled), we leave the GraphQL server's own listener untouched (zero change).
15
- *
16
- * @module @memberjunction/server/telephony
17
- */
18
- /** path → the `{ noServer:true }` WebSocketServer that owns it. */
19
- const mediaRoutes = new Map();
20
- /** Registers a media WebSocket route. Called by each vendor router's attach step. */
21
- export function RegisterMediaUpgradeRoute(path, wss) {
22
- mediaRoutes.set(path, wss);
23
- }
24
- /**
25
- * Whether an upgrade request's pathname should be routed to the GraphQL subscriptions socket.
26
- *
27
- * `GRAPHQL_ROOT_PATH` defaults to `/` (see `config.ts` / the docker README). On the HTTP side that
28
- * default is invisible — Express's `app.use(graphqlRootPath, ...)` mounts with PREFIX matching, so a
29
- * request to `/graphql` lands on the GraphQL middleware even when the configured root is the bare `/`.
30
- * A WebSocket upgrade has no such prefix-matching fallback: it needs an exact pathname match. With no
31
- * override, a stock install's default root (`/`) never matches the shipped MJExplorer client's hardcoded
32
- * `GRAPHQL_WS_URI` (`ws://<host>/graphql`), so every subscription upgrade gets silently rejected — surfaces
33
- * client-side as "WebSocket handshake: Unexpected response code: 400". Accept the conventional `/graphql`
34
- * suffix as an alias of the bare root so a stock install works out of the box; a non-default `graphqlPath`
35
- * (e.g. `/api`) keeps requiring an exact match, unchanged.
36
- */
37
- export function IsGraphQLWsPath(pathname, graphqlPath) {
38
- return pathname === graphqlPath || (graphqlPath === '/' && pathname === '/graphql');
39
- }
40
- /**
41
- * Installs the single path-routing `upgrade` dispatcher. No-op when no media routes are registered, so
42
- * the GraphQL server keeps its own listener untouched. Otherwise it removes the auto-attached listeners
43
- * (GraphQL's included) and dispatches every upgrade by pathname — GraphQL on `graphqlPath`, each media
44
- * socket on its registered path; unknown paths are destroyed.
45
- *
46
- * @param httpServer the shared HTTP server.
47
- * @param graphqlWss the GraphQL subscriptions WebSocketServer (kept, re-dispatched on its path).
48
- * @param graphqlPath the GraphQL root path (the path GraphQL's socket listens on).
49
- */
50
- export function InstallMediaUpgradeDispatcher(httpServer, graphqlWss, graphqlPath) {
51
- if (mediaRoutes.size === 0) {
52
- return;
53
- }
54
- httpServer.removeAllListeners('upgrade');
55
- const dispatch = (wss, req, socket, head) => {
56
- wss.handleUpgrade(req, socket, head, (ws) => wss.emit('connection', ws, req));
57
- };
58
- httpServer.on('upgrade', (req, socket, head) => {
59
- const pathname = (req.url ?? '').split('?')[0];
60
- if (IsGraphQLWsPath(pathname, graphqlPath)) {
61
- dispatch(graphqlWss, req, socket, head);
62
- return;
63
- }
64
- const wss = mediaRoutes.get(pathname);
65
- if (wss) {
66
- dispatch(wss, req, socket, head);
67
- return;
68
- }
69
- socket.destroy();
70
- });
71
- }
72
- //# sourceMappingURL=media-upgrade-router.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"media-upgrade-router.js","sourceRoot":"","sources":["../../src/telephony/media-upgrade-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,mEAAmE;AACnE,MAAM,WAAW,GAAG,IAAI,GAAG,EAA2B,CAAC;AAEvD,qFAAqF;AACrF,MAAM,UAAU,yBAAyB,CAAC,IAAY,EAAE,GAAoB;IACxE,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,WAAmB;IACjE,OAAO,QAAQ,KAAK,WAAW,IAAI,CAAC,WAAW,KAAK,GAAG,IAAI,QAAQ,KAAK,UAAU,CAAC,CAAC;AACxF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,6BAA6B,CAAC,UAAsB,EAAE,UAA2B,EAAE,WAAmB;IAClH,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;IACX,CAAC;IACD,UAAU,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,CAAC,GAAoB,EAAE,GAAoB,EAAE,MAAc,EAAE,IAAY,EAAQ,EAAE;QAChG,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAClF,CAAC,CAAC;IACF,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAoB,EAAE,MAAc,EAAE,IAAY,EAAE,EAAE;QAC5E,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,IAAI,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC;YACzC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YACxC,OAAO;QACX,CAAC;QACD,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,GAAG,EAAE,CAAC;YACN,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YACjC,OAAO;QACX,CAAC;QACD,MAAM,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;AACP,CAAC"}