@webex/internal-plugin-llm 3.12.0-task-refactor.1 → 3.12.0-webex-services-ready.1

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/dist/llm.js CHANGED
@@ -1,24 +1,35 @@
1
1
  "use strict";
2
2
 
3
3
  var _Reflect$construct = require("@babel/runtime-corejs2/core-js/reflect/construct");
4
+ var _Array$from = require("@babel/runtime-corejs2/core-js/array/from");
5
+ var _Symbol = require("@babel/runtime-corejs2/core-js/symbol");
6
+ var _Symbol$iterator = require("@babel/runtime-corejs2/core-js/symbol/iterator");
7
+ var _Array$isArray = require("@babel/runtime-corejs2/core-js/array/is-array");
4
8
  var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
5
9
  var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
6
10
  _Object$defineProperty(exports, "__esModule", {
7
11
  value: true
8
12
  });
9
13
  exports.default = exports.config = void 0;
10
- var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
14
+ var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
15
+ var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map"));
16
+ var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
17
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
18
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
11
19
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
20
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
12
21
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/possibleConstructorReturn"));
13
22
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/getPrototypeOf"));
14
23
  var _inherits2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/inherits"));
15
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
24
+ var _defineProperty3 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
16
25
  var _internalPluginMercury = _interopRequireDefault(require("@webex/internal-plugin-mercury"));
17
26
  var _constants = require("./constants");
27
+ var _llm = require("./llm.types");
28
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof _Symbol && r[_Symbol$iterator] || r["@@iterator"]; if (!t) { if (_Array$isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
29
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? _Array$from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
30
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
18
31
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? _Reflect$construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
19
- function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /* eslint-disable consistent-return */
20
- // eslint-disable-next-line no-unused-vars
21
-
32
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /* eslint-disable consistent-return */ // eslint-disable-next-line no-unused-vars
22
33
  var config = exports.config = {
23
34
  llm: {
24
35
  /**
@@ -61,95 +72,519 @@ var LLMChannel = exports.default = /*#__PURE__*/function (_ref) {
61
72
  args[_key] = arguments[_key];
62
73
  }
63
74
  _this = _callSuper(this, LLMChannel, [].concat(args));
64
- (0, _defineProperty2.default)(_this, "namespace", _constants.LLM);
75
+ (0, _defineProperty3.default)(_this, "namespace", _constants.LLM);
76
+ (0, _defineProperty3.default)(_this, "defaultSessionId", _constants.LLM_DEFAULT_SESSION);
65
77
  /**
66
- * If the LLM plugin has been registered and listening
67
- * @instance
68
- * @type {Boolean}
69
- * @public
78
+ * Map to store connection-specific data for multiple LLM connections
79
+ * Key: sessionId
80
+ * @private
81
+ * @type {Map<string, {webSocketUrl?: string; binding?: string; locusUrl?: string; datachannelUrl?: string}>}
70
82
  */
71
- (0, _defineProperty2.default)(_this, "webSocketUrl", void 0);
72
- (0, _defineProperty2.default)(_this, "binding", void 0);
73
- (0, _defineProperty2.default)(_this, "locusUrl", void 0);
74
- (0, _defineProperty2.default)(_this, "datachannelUrl", void 0);
83
+ (0, _defineProperty3.default)(_this, "connections", new _map.default());
84
+ // Session-keyed token cache is intentionally decoupled from connection state.
85
+ // Disconnecting a socket session must not implicitly wipe token cache.
86
+ (0, _defineProperty3.default)(_this, "datachannelTokens", (0, _defineProperty3.default)((0, _defineProperty3.default)({}, _llm.DataChannelTokenType.Default, undefined), _llm.DataChannelTokenType.PracticeSession, undefined));
75
87
  /**
76
88
  * Register to the websocket
77
89
  * @param {string} llmSocketUrl
90
+ * @param {string} datachannelToken
91
+ * @param {string} sessionId - Connection identifier
78
92
  * @returns {Promise<void>}
79
93
  */
80
- (0, _defineProperty2.default)(_this, "register", function (llmSocketUrl) {
81
- return _this.request({
82
- method: 'POST',
83
- url: llmSocketUrl,
84
- body: {
85
- deviceUrl: _this.webex.internal.device.url
86
- }
87
- }).then(function (res) {
88
- _this.webSocketUrl = res.body.webSocketUrl;
89
- _this.binding = res.body.binding;
90
- }).catch(function (error) {
91
- _this.logger.error("Error connecting to websocket: ".concat(error));
92
- throw error;
93
- });
94
- });
94
+ (0, _defineProperty3.default)(_this, "register", /*#__PURE__*/function () {
95
+ var _ref2 = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee(llmSocketUrl, datachannelToken) {
96
+ var sessionId,
97
+ isDataChannelTokenEnabled,
98
+ _args = arguments;
99
+ return _regenerator.default.wrap(function (_context) {
100
+ while (1) switch (_context.prev = _context.next) {
101
+ case 0:
102
+ sessionId = _args.length > 2 && _args[2] !== undefined ? _args[2] : _constants.LLM_DEFAULT_SESSION;
103
+ _context.next = 1;
104
+ return _this.isDataChannelTokenEnabled();
105
+ case 1:
106
+ isDataChannelTokenEnabled = _context.sent;
107
+ return _context.abrupt("return", _this.request({
108
+ method: 'POST',
109
+ url: llmSocketUrl,
110
+ body: {
111
+ deviceUrl: _this.webex.internal.device.url
112
+ },
113
+ headers: isDataChannelTokenEnabled && datachannelToken ? {
114
+ 'Data-Channel-Auth-Token': datachannelToken
115
+ } : {}
116
+ }).then(function (res) {
117
+ // Get or create connection data
118
+ var sessionData = _this.connections.get(sessionId) || {};
119
+ sessionData.webSocketUrl = res.body.webSocketUrl;
120
+ sessionData.binding = res.body.binding;
121
+ _this.connections.set(sessionId, sessionData);
122
+ }).catch(function (error) {
123
+ _this.logger.error("Error connecting to websocket for ".concat(sessionId, ": ").concat(error));
124
+ throw error;
125
+ }));
126
+ case 2:
127
+ case "end":
128
+ return _context.stop();
129
+ }
130
+ }, _callee);
131
+ }));
132
+ return function (_x, _x2) {
133
+ return _ref2.apply(this, arguments);
134
+ };
135
+ }());
95
136
  /**
96
137
  * Register and connect to the websocket
97
138
  * @param {string} locusUrl
98
139
  * @param {string} datachannelUrl
140
+ * @param {string} datachannelToken
141
+ * @param {string} sessionId - Connection identifier
99
142
  * @returns {Promise<void>}
100
143
  */
101
- (0, _defineProperty2.default)(_this, "registerAndConnect", function (locusUrl, datachannelUrl) {
102
- return _this.register(datachannelUrl).then(function () {
103
- if (!locusUrl || !datachannelUrl) return undefined;
104
- _this.locusUrl = locusUrl;
105
- _this.datachannelUrl = datachannelUrl;
106
- _this.connect(_this.webSocketUrl);
107
- });
144
+ (0, _defineProperty3.default)(_this, "registerAndConnect", function (locusUrl, datachannelUrl, datachannelToken) {
145
+ var sessionId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _constants.LLM_DEFAULT_SESSION;
146
+ // Pre-populate locusUrl and datachannelUrl before register() fires the
147
+ // HTTP POST, so that any token refresh triggered during registration can
148
+ // be routed via connections without falling back to a locusInfo URL scan.
149
+ if (locusUrl && datachannelUrl) {
150
+ var sessionData = _this.connections.get(sessionId) || {};
151
+ sessionData.locusUrl = locusUrl;
152
+ sessionData.datachannelUrl = datachannelUrl;
153
+ _this.connections.set(sessionId, sessionData);
154
+ }
155
+ return _this.register(datachannelUrl, datachannelToken, sessionId).then(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
156
+ var sessionData, isDataChannelTokenEnabled, connectUrl;
157
+ return _regenerator.default.wrap(function (_context2) {
158
+ while (1) switch (_context2.prev = _context2.next) {
159
+ case 0:
160
+ if (!(!locusUrl || !datachannelUrl)) {
161
+ _context2.next = 1;
162
+ break;
163
+ }
164
+ return _context2.abrupt("return", undefined);
165
+ case 1:
166
+ // locusUrl and datachannelUrl were pre-populated before register(); here
167
+ // we only need to read the existing session data to get webSocketUrl/binding
168
+ // that register() filled in.
169
+ sessionData = _this.connections.get(sessionId) || {};
170
+ _context2.next = 2;
171
+ return _this.isDataChannelTokenEnabled();
172
+ case 2:
173
+ isDataChannelTokenEnabled = _context2.sent;
174
+ connectUrl = isDataChannelTokenEnabled ? LLMChannel.buildUrlWithAwareSubchannels(sessionData.webSocketUrl, _constants.AWARE_DATA_CHANNEL) : sessionData.webSocketUrl;
175
+ return _context2.abrupt("return", _this.connect(connectUrl, sessionId));
176
+ case 3:
177
+ case "end":
178
+ return _context2.stop();
179
+ }
180
+ }, _callee2);
181
+ })));
108
182
  });
109
183
  /**
110
184
  * Tells if LLM socket is connected
185
+ * @param {string} sessionId - Connection identifier
111
186
  * @returns {boolean} connected
112
187
  */
113
- (0, _defineProperty2.default)(_this, "isConnected", function () {
114
- return _this.connected;
188
+ (0, _defineProperty3.default)(_this, "isConnected", function () {
189
+ var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.LLM_DEFAULT_SESSION;
190
+ var socket = _this.getSocket(sessionId);
191
+ return socket ? socket.connected : false;
115
192
  });
116
193
  /**
117
194
  * Tells if LLM socket is binding
195
+ * @param {string} sessionId - Connection identifier
118
196
  * @returns {string} binding
119
197
  */
120
- (0, _defineProperty2.default)(_this, "getBinding", function () {
121
- return _this.binding;
198
+ (0, _defineProperty3.default)(_this, "getBinding", function () {
199
+ var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.LLM_DEFAULT_SESSION;
200
+ var sessionData = _this.connections.get(sessionId);
201
+ return sessionData === null || sessionData === void 0 ? void 0 : sessionData.binding;
122
202
  });
123
203
  /**
124
204
  * Get Locus URL for the connection
205
+ * @param {string} sessionId - Connection identifier
125
206
  * @returns {string} locus Url
126
207
  */
127
- (0, _defineProperty2.default)(_this, "getLocusUrl", function () {
128
- return _this.locusUrl;
208
+ (0, _defineProperty3.default)(_this, "getLocusUrl", function () {
209
+ var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.LLM_DEFAULT_SESSION;
210
+ var sessionData = _this.connections.get(sessionId);
211
+ return sessionData === null || sessionData === void 0 ? void 0 : sessionData.locusUrl;
129
212
  });
130
213
  /**
131
214
  * Get data channel URL for the connection
215
+ * @param {string} sessionId - Connection identifier
132
216
  * @returns {string} data channel Url
133
217
  */
134
- (0, _defineProperty2.default)(_this, "getDatachannelUrl", function () {
135
- return _this.datachannelUrl;
218
+ (0, _defineProperty3.default)(_this, "getDatachannelUrl", function () {
219
+ var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.LLM_DEFAULT_SESSION;
220
+ var sessionData = _this.connections.get(sessionId);
221
+ return sessionData === null || sessionData === void 0 ? void 0 : sessionData.datachannelUrl;
222
+ });
223
+ /**
224
+ * Set the owner meeting ID for a given LLM session. Used by the meetings
225
+ * plugin to tag which Meeting instance currently owns the (default) LLM
226
+ * connection so that other Meeting instances can avoid disconnecting or
227
+ * re-initializing a connection they do not own.
228
+ *
229
+ * Does NOT create a connections entry if one does not already exist — this
230
+ * method is a no-op when there is no active session data. Callers should
231
+ * invoke it after a successful `registerAndConnect` or during an explicit
232
+ * ownership handoff.
233
+ *
234
+ * @param {string | undefined} ownerMeetingId - Meeting ID (or undefined to clear)
235
+ * @param {string} sessionId - Connection identifier (defaults to default session)
236
+ * @returns {void}
237
+ */
238
+ (0, _defineProperty3.default)(_this, "setOwnerMeetingId", function (ownerMeetingId) {
239
+ var sessionId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _constants.LLM_DEFAULT_SESSION;
240
+ var sessionData = _this.connections.get(sessionId);
241
+ if (!sessionData) {
242
+ return;
243
+ }
244
+ sessionData.ownerMeetingId = ownerMeetingId;
245
+ _this.connections.set(sessionId, sessionData);
246
+ });
247
+ /**
248
+ * Get the owner meeting ID currently associated with an LLM session.
249
+ * Returns undefined when no owner has been assigned (e.g. before the
250
+ * first successful `registerAndConnect`, or after `disconnectLLM`).
251
+ *
252
+ * @param {string} sessionId - Connection identifier (defaults to default session)
253
+ * @returns {string | undefined} ownerMeetingId
254
+ */
255
+ (0, _defineProperty3.default)(_this, "getOwnerMeetingId", function () {
256
+ var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.LLM_DEFAULT_SESSION;
257
+ var sessionData = _this.connections.get(sessionId);
258
+ return sessionData === null || sessionData === void 0 ? void 0 : sessionData.ownerMeetingId;
259
+ });
260
+ /**
261
+ * Resolve ownership information for an LLM session.
262
+ *
263
+ * Rules:
264
+ * - no current owner => caller may proceed
265
+ * - caller has no identity to assert => treat as owner
266
+ * - otherwise caller must match current owner
267
+ *
268
+ * @param {string | undefined} ownerMeetingId - Candidate owner to evaluate
269
+ * @param {string} sessionId - Connection identifier (defaults to default session)
270
+ * @returns {{currentOwner: (string|undefined), isOwner: boolean}}
271
+ */
272
+ (0, _defineProperty3.default)(_this, "resolveSessionOwnership", function (ownerMeetingId) {
273
+ var sessionId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _constants.LLM_DEFAULT_SESSION;
274
+ var currentOwner = _this.getOwnerMeetingId(sessionId);
275
+ var isOwner = !currentOwner || !ownerMeetingId || currentOwner === ownerMeetingId;
276
+ return {
277
+ currentOwner: currentOwner,
278
+ isOwner: isOwner
279
+ };
280
+ });
281
+ /**
282
+ * Get data channel token for the connection
283
+ * @param {DataChannelTokenType|string} tokenKey
284
+ * @param {string | undefined} ownerMeetingId - Meeting id asserting read ownership
285
+ * @returns {string | undefined} data channel token
286
+ */
287
+ (0, _defineProperty3.default)(_this, "getDatachannelToken", function (tokenKey, ownerMeetingId) {
288
+ var resolvedTokenKey = tokenKey !== null && tokenKey !== void 0 ? tokenKey : _llm.DataChannelTokenType.Default;
289
+ var _this$resolveSessionO = _this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey),
290
+ currentOwner = _this$resolveSessionO.currentOwner,
291
+ isOwner = _this$resolveSessionO.isOwner;
292
+ if (!isOwner) {
293
+ _this.logger.info("llm#getDatachannelToken --> skip read for session ".concat(resolvedTokenKey, "; owned by ").concat(currentOwner, ", candidate ").concat(ownerMeetingId));
294
+ return undefined;
295
+ }
296
+ return _this.datachannelTokens[resolvedTokenKey];
297
+ });
298
+ /**
299
+ * Set data channel token for the connection
300
+ * @param {string} datachannelToken - data channel token
301
+ * @param {DataChannelTokenType|string} [tokenKey]
302
+ * @param {string | undefined} ownerMeetingId - Meeting id asserting write ownership
303
+ * @returns {void}
304
+ */
305
+ (0, _defineProperty3.default)(_this, "setDatachannelToken", function (datachannelToken, tokenKey, ownerMeetingId) {
306
+ var resolvedTokenKey = tokenKey !== null && tokenKey !== void 0 ? tokenKey : _llm.DataChannelTokenType.Default;
307
+ var _this$resolveSessionO2 = _this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey),
308
+ currentOwner = _this$resolveSessionO2.currentOwner,
309
+ isOwner = _this$resolveSessionO2.isOwner;
310
+ if (!isOwner) {
311
+ _this.logger.info("llm#setDatachannelToken --> skip write for session ".concat(resolvedTokenKey, "; owned by ").concat(currentOwner, ", candidate ").concat(ownerMeetingId));
312
+ return;
313
+ }
314
+ _this.datachannelTokens[resolvedTokenKey] = datachannelToken;
315
+ });
316
+ /**
317
+ * Clears a single session's data channel token.
318
+ * @param {DataChannelTokenType|string} tokenKey
319
+ * @param {string} ownerMeetingId - Meeting id asserting delete ownership
320
+ * @returns {void}
321
+ */
322
+ (0, _defineProperty3.default)(_this, "clearDatachannelToken", function (tokenKey, ownerMeetingId) {
323
+ var resolvedTokenKey = tokenKey;
324
+ var _this$resolveSessionO3 = _this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey),
325
+ currentOwner = _this$resolveSessionO3.currentOwner,
326
+ isOwner = _this$resolveSessionO3.isOwner;
327
+ if (!isOwner) {
328
+ _this.logger.info("llm#clearDatachannelToken --> skip clear for session ".concat(resolvedTokenKey, "; owned by ").concat(currentOwner, ", candidate ").concat(ownerMeetingId));
329
+ return;
330
+ }
331
+ _this.datachannelTokens[resolvedTokenKey] = undefined;
332
+ delete _this.datachannelTokens[resolvedTokenKey];
136
333
  });
137
334
  /**
138
335
  * Disconnects websocket connection
139
336
  * @param {{code: number, reason: string}} options - The disconnect option object with code and reason
337
+ * @param {string} sessionId - Connection identifier
338
+ * @param {string} ownerMeetingId - Meeting id asserting disconnect ownership
339
+ * @returns {Promise<boolean>} True when disconnect was performed, false when skipped
340
+ */
341
+ (0, _defineProperty3.default)(_this, "disconnectLLM", function (options, sessionId, ownerMeetingId) {
342
+ var resolvedSessionId = sessionId !== null && sessionId !== void 0 ? sessionId : _constants.LLM_DEFAULT_SESSION;
343
+
344
+ // Backward-compat path: historically callers could omit ownerMeetingId
345
+ // (and sometimes sessionId). Reuse current owner when available so legacy
346
+ // calls remain best-effort without throwing at teardown time.
347
+ var resolvedOwnerMeetingId = ownerMeetingId || _this.getOwnerMeetingId(resolvedSessionId);
348
+ if (!ownerMeetingId) {
349
+ _this.logger.warn("llm#disconnectLLM --> ownerMeetingId is omitted for session ".concat(resolvedSessionId, "; using legacy compatibility path"));
350
+ }
351
+ var _this$resolveSessionO4 = _this.resolveSessionOwnership(resolvedOwnerMeetingId, resolvedSessionId),
352
+ currentOwner = _this$resolveSessionO4.currentOwner,
353
+ isOwner = _this$resolveSessionO4.isOwner;
354
+ if (!isOwner) {
355
+ _this.logger.info("llm#disconnectLLM --> skip disconnect for session ".concat(resolvedSessionId, "; owned by ").concat(currentOwner, ", candidate ").concat(resolvedOwnerMeetingId));
356
+ return _promise.default.resolve(false);
357
+ }
358
+ return _this.disconnect(options, resolvedSessionId).then(function () {
359
+ // Clear owner tag before cleanup to ensure it's not lingering
360
+ // if another meeting claimed it during disconnect
361
+ _this.setOwnerMeetingId(undefined, resolvedSessionId);
362
+
363
+ // Clean up sessions data
364
+ _this.connections.delete(resolvedSessionId);
365
+ return true;
366
+ });
367
+ });
368
+ /**
369
+ * Disconnects all LLM websocket connections
370
+ * @param {{code: number, reason: string}} options - The disconnect option object with code and reason
140
371
  * @returns {Promise<void>}
141
372
  */
142
- (0, _defineProperty2.default)(_this, "disconnectLLM", function (options) {
143
- return _this.disconnect(options).then(function () {
144
- _this.locusUrl = undefined;
145
- _this.datachannelUrl = undefined;
146
- _this.binding = undefined;
147
- _this.webSocketUrl = undefined;
373
+ (0, _defineProperty3.default)(_this, "disconnectAllLLM", function (options) {
374
+ return _this.disconnectAll(options).then(function () {
375
+ // Clean up all connection data
376
+ _this.connections.clear();
148
377
  });
149
378
  });
379
+ /**
380
+ * Get all active LLM connections
381
+ * @returns {Map} Map of sessionId to session data
382
+ */
383
+ (0, _defineProperty3.default)(_this, "getAllConnections", function () {
384
+ return new _map.default(_this.connections);
385
+ });
150
386
  return _this;
151
387
  }
152
388
  (0, _inherits2.default)(LLMChannel, _ref);
153
- return (0, _createClass2.default)(LLMChannel);
389
+ return (0, _createClass2.default)(LLMChannel, [{
390
+ key: "setRefreshHandler",
391
+ value:
392
+ /**
393
+ * Set the handler used to refresh the DataChannel token
394
+ *
395
+ * @param {function} handler - Function that returns a refreshed token
396
+ * @param {string} [sessionId] - Connection identifier
397
+ * @param {string | undefined} ownerMeetingId - Meeting id asserting refresh-handler ownership
398
+ * @returns {void}
399
+ */
400
+ function setRefreshHandler(handler, sessionId, ownerMeetingId) {
401
+ var resolvedSessionId = sessionId !== null && sessionId !== void 0 ? sessionId : _constants.LLM_DEFAULT_SESSION;
402
+ var _this$resolveSessionO5 = this.resolveSessionOwnership(ownerMeetingId, resolvedSessionId),
403
+ currentOwner = _this$resolveSessionO5.currentOwner,
404
+ isOwner = _this$resolveSessionO5.isOwner;
405
+ if (!isOwner) {
406
+ this.logger.info("llm#setRefreshHandler --> skip write for session ".concat(resolvedSessionId, "; owned by ").concat(currentOwner, ", candidate ").concat(ownerMeetingId));
407
+ return;
408
+ }
409
+ var sessionData = this.connections.get(resolvedSessionId);
410
+ if (sessionData) {
411
+ sessionData.refreshHandler = handler;
412
+ if (ownerMeetingId) {
413
+ sessionData.ownerMeetingId = ownerMeetingId;
414
+ }
415
+ return;
416
+ }
417
+
418
+ // Intentionally allow a pre-connection session shape here.
419
+ // Some flows inject refreshHandler before register/connect so token refresh
420
+ // is already wired when the socket lifecycle starts. register()/
421
+ // registerAndConnect() will later fill webSocketUrl/binding/locusUrl/
422
+ // datachannelUrl into this same session entry.
423
+ this.connections.set(resolvedSessionId, {
424
+ refreshHandler: handler,
425
+ ownerMeetingId: ownerMeetingId
426
+ });
427
+ }
428
+
429
+ /**
430
+ * Refresh the data channel token using the injected handler.
431
+ * Logs a descriptive error if the handler is missing or fails.
432
+ * @param {string} sessionId - Connection identifier (defaults to default session)
433
+ * @returns {Promise<string>} The refreshed token.
434
+ */
435
+ }, {
436
+ key: "refreshDataChannelToken",
437
+ value: (function () {
438
+ var _refreshDataChannelToken = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
439
+ var _this$connections$get;
440
+ var sessionId,
441
+ refreshHandler,
442
+ res,
443
+ _args3 = arguments,
444
+ _t;
445
+ return _regenerator.default.wrap(function (_context3) {
446
+ while (1) switch (_context3.prev = _context3.next) {
447
+ case 0:
448
+ sessionId = _args3.length > 0 && _args3[0] !== undefined ? _args3[0] : _constants.LLM_DEFAULT_SESSION;
449
+ refreshHandler = (_this$connections$get = this.connections.get(sessionId)) === null || _this$connections$get === void 0 ? void 0 : _this$connections$get.refreshHandler;
450
+ if (refreshHandler) {
451
+ _context3.next = 1;
452
+ break;
453
+ }
454
+ this.logger.warn("llm#refreshDataChannelToken --> LLM refreshHandler is not set for session ".concat(sessionId, ", skipping token refresh"));
455
+ return _context3.abrupt("return", null);
456
+ case 1:
457
+ _context3.prev = 1;
458
+ _context3.next = 2;
459
+ return refreshHandler();
460
+ case 2:
461
+ res = _context3.sent;
462
+ return _context3.abrupt("return", res);
463
+ case 3:
464
+ _context3.prev = 3;
465
+ _t = _context3["catch"](1);
466
+ this.logger.warn("llm#refreshDataChannelToken --> DataChannel token refresh failed (likely locus changed or participant left): ".concat((_t === null || _t === void 0 ? void 0 : _t.message) || _t));
467
+ return _context3.abrupt("return", null);
468
+ case 4:
469
+ case "end":
470
+ return _context3.stop();
471
+ }
472
+ }, _callee3, this, [[1, 3]]);
473
+ }));
474
+ function refreshDataChannelToken() {
475
+ return _refreshDataChannelToken.apply(this, arguments);
476
+ }
477
+ return refreshDataChannelToken;
478
+ }())
479
+ }, {
480
+ key: "getLocusUrlByDatachannelUrl",
481
+ value:
482
+ /**
483
+ * Look up the locusUrl associated with a datachannel request URL.
484
+ * Iterates all active LLM sessions and returns the locusUrl of the
485
+ * session whose stored datachannelUrl is a prefix of the given request URL.
486
+ *
487
+ * @param {string} requestUrl - The in-flight request URL to match
488
+ * @returns {string | undefined} The matching locusUrl, or undefined if not found
489
+ */
490
+ function getLocusUrlByDatachannelUrl(requestUrl) {
491
+ var _iterator = _createForOfIteratorHelper(this.connections),
492
+ _step;
493
+ try {
494
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
495
+ var _step$value = (0, _slicedToArray2.default)(_step.value, 2),
496
+ connection = _step$value[1];
497
+ if (connection.datachannelUrl && LLMChannel.matchesDatachannelRequestUrl(requestUrl, connection.datachannelUrl)) {
498
+ return connection.locusUrl;
499
+ }
500
+ }
501
+ } catch (err) {
502
+ _iterator.e(err);
503
+ } finally {
504
+ _iterator.f();
505
+ }
506
+ return undefined;
507
+ }
508
+
509
+ /**
510
+ * Look up the sessionId associated with a datachannel request URL.
511
+ * Iterates all active LLM sessions and returns the sessionId whose
512
+ * stored datachannelUrl is a prefix of the given request URL.
513
+ *
514
+ * @param {string} requestUrl - The in-flight request URL to match
515
+ * @returns {string | undefined} The matching sessionId, or undefined if not found
516
+ */
517
+ }, {
518
+ key: "getSessionIdByDatachannelUrl",
519
+ value: function getSessionIdByDatachannelUrl(requestUrl) {
520
+ var _iterator2 = _createForOfIteratorHelper(this.connections),
521
+ _step2;
522
+ try {
523
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
524
+ var _step2$value = (0, _slicedToArray2.default)(_step2.value, 2),
525
+ sessionId = _step2$value[0],
526
+ connection = _step2$value[1];
527
+ if (connection.datachannelUrl && LLMChannel.matchesDatachannelRequestUrl(requestUrl, connection.datachannelUrl)) {
528
+ return sessionId;
529
+ }
530
+ }
531
+ } catch (err) {
532
+ _iterator2.e(err);
533
+ } finally {
534
+ _iterator2.f();
535
+ }
536
+ return undefined;
537
+ }
538
+
539
+ /**
540
+ * Matches a request URL to a stored datachannel registration URL.
541
+ * Host can differ (e.g. rewritten by hostmap interceptor), so we first
542
+ * try full URL prefix and then fall back to pathname prefix.
543
+ * @param {string} requestUrl
544
+ * @param {string} registrationUrl
545
+ * @returns {boolean}
546
+ */
547
+ }, {
548
+ key: "isDataChannelTokenEnabled",
549
+ value:
550
+ /**
551
+ * Returns true if data channel token is enabled, false otherwise
552
+ * @returns {Promise<boolean>} resolves with true if data channel token is enabled
553
+ */
554
+ function isDataChannelTokenEnabled() {
555
+ // @ts-ignore
556
+ return this.webex.internal.feature.getFeature('developer', _constants.DATA_CHANNEL_WITH_JWT_TOKEN);
557
+ }
558
+
559
+ /**
560
+ * Builds a WebSocket URL with the `subscriptionAwareSubchannels` query parameter.
561
+ *
562
+ * @param {string} baseUrl - The original WebSocket URL.
563
+ * @param {string[]} subchannels - List of subchannels to declare as subscription-aware.
564
+ * @returns {string} The final URL with updated query parameters.
565
+ */
566
+ }], [{
567
+ key: "matchesDatachannelRequestUrl",
568
+ value: function matchesDatachannelRequestUrl(requestUrl, registrationUrl) {
569
+ if (!requestUrl || !registrationUrl) {
570
+ return false;
571
+ }
572
+ if (requestUrl.startsWith(registrationUrl)) {
573
+ return true;
574
+ }
575
+ try {
576
+ var request = new URL(requestUrl);
577
+ var registration = new URL(registrationUrl);
578
+ return request.pathname.startsWith(registration.pathname);
579
+ } catch (error) {
580
+ return false;
581
+ }
582
+ }
583
+ }]);
154
584
  }(_internalPluginMercury.default);
585
+ (0, _defineProperty3.default)(LLMChannel, "buildUrlWithAwareSubchannels", function (baseUrl, subchannels) {
586
+ var urlObj = new URL(baseUrl);
587
+ urlObj.searchParams.set(_constants.SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM, subchannels.join(','));
588
+ return urlObj.toString();
589
+ });
155
590
  //# sourceMappingURL=llm.js.map