@unboundcx/sdk 4.13.26 → 4.13.28
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/index.js +1 -0
- package/package.json +1 -1
- package/services/webchat/VisitorService.js +359 -0
- package/services/webchat.js +52 -1
package/index.js
CHANGED
|
@@ -304,6 +304,7 @@ export {
|
|
|
304
304
|
WebchatWidgetsService,
|
|
305
305
|
WebchatWidgetKeysService,
|
|
306
306
|
} from './services/webchat.js';
|
|
307
|
+
export { WebchatVisitorService } from './services/webchat/VisitorService.js';
|
|
307
308
|
export { ExternalOAuthService } from './services/externalOAuth.js';
|
|
308
309
|
export { GoogleCalendarService } from './services/googleCalendar.js';
|
|
309
310
|
export { DriveService } from './services/drive.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundcx/sdk",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.28",
|
|
4
4
|
"description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { internalRequest } from '../../base.js';
|
|
2
|
+
|
|
3
|
+
// Public, unauthenticated visitor surface -- `sdk.webchat.visitor`.
|
|
4
|
+
//
|
|
5
|
+
// Mirrors the app1-webchat-embed app's own request shapes
|
|
6
|
+
// (app1-webchat-embed/src/lib/utils/webchatApi.js) exactly, so this is a
|
|
7
|
+
// drop-in for that fetch wrapper, not a parallel client with different
|
|
8
|
+
// semantics: same paths under bare /webchat/:widgetId/*, same `embedGrant`
|
|
9
|
+
// body field on session create, same Authorization-header-or-body/query
|
|
10
|
+
// `token` fallback the API controllers accept.
|
|
11
|
+
//
|
|
12
|
+
// No agent auth: the sdk instance backing this only needs `namespace` (or
|
|
13
|
+
// a custom `baseURL`) set at construction -- never `sdk.token`. Every
|
|
14
|
+
// method forces HTTP (esign.public precedent, base.js `forceFetch`) since
|
|
15
|
+
// these are one-shot fetches from a customer page or a custom UI, never
|
|
16
|
+
// NATS-transport traffic.
|
|
17
|
+
function authHeaders(token) {
|
|
18
|
+
return token ? { headers: { Authorization: `Bearer ${token}` } } : {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class WebchatVisitorSessionService {
|
|
22
|
+
constructor(sdk) {
|
|
23
|
+
this.sdk = sdk;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Start a new conversation. Requires an `embedGrant` minted by
|
|
28
|
+
* `GET /webchat/:widgetId/grant` (or by loader.js for the JS-snippet
|
|
29
|
+
* path) -- a custom UI on an allowlisted origin calls `grant()` first.
|
|
30
|
+
* @param {Object} params
|
|
31
|
+
* @param {string} params.widgetId
|
|
32
|
+
* @param {string} params.embedGrant
|
|
33
|
+
* @param {Object} [params.visitorData] - `{...fields, hash}`; `hash` is the
|
|
34
|
+
* HMAC-SHA256 (identitySecret) over the sorted `key=value&...` fields
|
|
35
|
+
* string, matching verifyVisitorIdentity.js's canonicalization exactly.
|
|
36
|
+
* Omit `hash` (or the whole object) to send fully unverified metadata.
|
|
37
|
+
* @param {string} [params.pageUrl]
|
|
38
|
+
* @returns {Promise<{engagementSessionId:string, token:string, resumeToken:string, status:'created'}>}
|
|
39
|
+
*/
|
|
40
|
+
async create({ widgetId, embedGrant, visitorData, pageUrl } = {}) {
|
|
41
|
+
this.sdk.validateParams(
|
|
42
|
+
{ widgetId, embedGrant },
|
|
43
|
+
{
|
|
44
|
+
widgetId: { type: 'string', required: true },
|
|
45
|
+
embedGrant: { type: 'string', required: true },
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
return internalRequest(
|
|
49
|
+
this.sdk,
|
|
50
|
+
`/webchat/${widgetId}/session`,
|
|
51
|
+
'POST',
|
|
52
|
+
{ body: { embedGrant, visitorData, pageUrl } },
|
|
53
|
+
true,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resume an existing conversation from a previously-issued resume token
|
|
59
|
+
* (same endpoint as `create`; the resumeToken body field selects the
|
|
60
|
+
* resume path server-side).
|
|
61
|
+
* @param {Object} params
|
|
62
|
+
* @param {string} params.widgetId
|
|
63
|
+
* @param {string} params.resumeToken
|
|
64
|
+
* @returns {Promise<{engagementSessionId:string, token:string, resumeToken:string, status:'resumed'}>}
|
|
65
|
+
*/
|
|
66
|
+
async resume({ widgetId, resumeToken } = {}) {
|
|
67
|
+
this.sdk.validateParams(
|
|
68
|
+
{ widgetId, resumeToken },
|
|
69
|
+
{
|
|
70
|
+
widgetId: { type: 'string', required: true },
|
|
71
|
+
resumeToken: { type: 'string', required: true },
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
return internalRequest(
|
|
75
|
+
this.sdk,
|
|
76
|
+
`/webchat/${widgetId}/session`,
|
|
77
|
+
'POST',
|
|
78
|
+
{ body: { resumeToken } },
|
|
79
|
+
true,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Visitor-initiated completion (idle-timeout path closes the same way,
|
|
85
|
+
* server-side).
|
|
86
|
+
* @param {Object} params
|
|
87
|
+
* @param {string} params.widgetId
|
|
88
|
+
* @param {string} params.token - Session JWT from create/resume.
|
|
89
|
+
* @returns {Promise<{status:'completed'}>}
|
|
90
|
+
*/
|
|
91
|
+
async end({ widgetId, token } = {}) {
|
|
92
|
+
this.sdk.validateParams(
|
|
93
|
+
{ widgetId, token },
|
|
94
|
+
{
|
|
95
|
+
widgetId: { type: 'string', required: true },
|
|
96
|
+
token: { type: 'string', required: true },
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
return internalRequest(
|
|
100
|
+
this.sdk,
|
|
101
|
+
`/webchat/${widgetId}/session/end`,
|
|
102
|
+
'POST',
|
|
103
|
+
{ ...authHeaders(token) },
|
|
104
|
+
true,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class WebchatVisitorMessagesService {
|
|
110
|
+
constructor(sdk) {
|
|
111
|
+
this.sdk = sdk;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* History for the session's own conversation (session JWT scopes
|
|
116
|
+
* engagementSessionId server-side -- never pass one here).
|
|
117
|
+
* @param {Object} params
|
|
118
|
+
* @param {string} params.widgetId
|
|
119
|
+
* @param {string} params.token
|
|
120
|
+
* @param {string} [params.before] - Cursor: messages created before this.
|
|
121
|
+
* @param {number} [params.limit]
|
|
122
|
+
* @returns {Promise<{messages:Object[]}>}
|
|
123
|
+
*/
|
|
124
|
+
async list({ widgetId, token, before, limit } = {}) {
|
|
125
|
+
this.sdk.validateParams(
|
|
126
|
+
{ widgetId, token },
|
|
127
|
+
{
|
|
128
|
+
widgetId: { type: 'string', required: true },
|
|
129
|
+
token: { type: 'string', required: true },
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
return internalRequest(
|
|
133
|
+
this.sdk,
|
|
134
|
+
`/webchat/${widgetId}/messages`,
|
|
135
|
+
'GET',
|
|
136
|
+
{
|
|
137
|
+
query: {
|
|
138
|
+
...(before !== undefined && before !== null ? { before } : {}),
|
|
139
|
+
...(limit !== undefined && limit !== null ? { limit } : {}),
|
|
140
|
+
},
|
|
141
|
+
...authHeaders(token),
|
|
142
|
+
},
|
|
143
|
+
true,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Send a visitor message (direction is always 'visitor' server-side).
|
|
149
|
+
* @param {Object} params
|
|
150
|
+
* @param {string} params.widgetId
|
|
151
|
+
* @param {string} params.token
|
|
152
|
+
* @param {string} params.message
|
|
153
|
+
* @param {Object[]} [params.media]
|
|
154
|
+
* @returns {Promise<{message:Object}>}
|
|
155
|
+
*/
|
|
156
|
+
async send({ widgetId, token, message, media } = {}) {
|
|
157
|
+
this.sdk.validateParams(
|
|
158
|
+
{ widgetId, token },
|
|
159
|
+
{
|
|
160
|
+
widgetId: { type: 'string', required: true },
|
|
161
|
+
token: { type: 'string', required: true },
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
return internalRequest(
|
|
165
|
+
this.sdk,
|
|
166
|
+
`/webchat/${widgetId}/messages`,
|
|
167
|
+
'POST',
|
|
168
|
+
{ body: { message, media }, ...authHeaders(token) },
|
|
169
|
+
true,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class WebchatVisitorFilesService {
|
|
175
|
+
constructor(sdk) {
|
|
176
|
+
this.sdk = sdk;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Upload a visitor file (streaming multer engine server-side; 403 when
|
|
181
|
+
* the widget has file upload disabled). Browser-only (needs `FormData`)
|
|
182
|
+
* -- unlike the embed app's own XHR upload, this has no progress event;
|
|
183
|
+
* a custom UI that needs a progress bar should keep using XHR directly
|
|
184
|
+
* against the same endpoint (see app1-webchat-embed/webchatApi.js) until
|
|
185
|
+
* the SDK grows an upload-progress hook.
|
|
186
|
+
* @param {Object} params
|
|
187
|
+
* @param {string} params.widgetId
|
|
188
|
+
* @param {string} params.token
|
|
189
|
+
* @param {File|Blob} params.file
|
|
190
|
+
* @returns {Promise<{file:Object, message:Object}>}
|
|
191
|
+
*/
|
|
192
|
+
async upload({ widgetId, token, file } = {}) {
|
|
193
|
+
this.sdk.validateParams(
|
|
194
|
+
{ widgetId, token },
|
|
195
|
+
{
|
|
196
|
+
widgetId: { type: 'string', required: true },
|
|
197
|
+
token: { type: 'string', required: true },
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
if (typeof FormData === 'undefined' || !file) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
'webchat.visitor.files.upload :: a browser File/Blob and FormData support are required',
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const body = new FormData();
|
|
206
|
+
body.append('file', file);
|
|
207
|
+
return internalRequest(
|
|
208
|
+
this.sdk,
|
|
209
|
+
`/webchat/${widgetId}/files`,
|
|
210
|
+
'POST',
|
|
211
|
+
{ body, ...authHeaders(token) },
|
|
212
|
+
true,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Direct download URL for a previously-uploaded file (token as a query
|
|
218
|
+
* param -- iframe `<a>`/`<img>` tags can't set an Authorization header).
|
|
219
|
+
* Not a request -- returns the URL string to link/navigate to.
|
|
220
|
+
* @param {Object} params
|
|
221
|
+
* @param {string} params.widgetId
|
|
222
|
+
* @param {string} params.fileId
|
|
223
|
+
* @param {string} params.token
|
|
224
|
+
* @returns {string}
|
|
225
|
+
*/
|
|
226
|
+
downloadUrl({ widgetId, fileId, token } = {}) {
|
|
227
|
+
this.sdk.validateParams(
|
|
228
|
+
{ widgetId, fileId, token },
|
|
229
|
+
{
|
|
230
|
+
widgetId: { type: 'string', required: true },
|
|
231
|
+
fileId: { type: 'string', required: true },
|
|
232
|
+
token: { type: 'string', required: true },
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
return `${this.sdk.fullUrl || this.sdk.baseURL}/webchat/${widgetId}/files/${fileId}?token=${encodeURIComponent(token)}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export class WebchatVisitorService {
|
|
240
|
+
constructor(sdk) {
|
|
241
|
+
this.sdk = sdk;
|
|
242
|
+
this.session = new WebchatVisitorSessionService(sdk);
|
|
243
|
+
this.messages = new WebchatVisitorMessagesService(sdk);
|
|
244
|
+
this.files = new WebchatVisitorFilesService(sdk);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Mint a short-TTL embedGrant for this page's real origin (server
|
|
249
|
+
* validates Origin/Referer against the widget's domainAllowlist) -- a
|
|
250
|
+
* custom UI calls this before `session.create`, same as loader.js does
|
|
251
|
+
* at iframe-open time.
|
|
252
|
+
* @param {string} widgetId
|
|
253
|
+
* @returns {Promise<{grant:string, exp:number}>}
|
|
254
|
+
*/
|
|
255
|
+
async grant(widgetId) {
|
|
256
|
+
this.sdk.validateParams(
|
|
257
|
+
{ widgetId },
|
|
258
|
+
{ widgetId: { type: 'string', required: true } },
|
|
259
|
+
);
|
|
260
|
+
return internalRequest(this.sdk, `/webchat/${widgetId}/grant`, 'GET', {}, true);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Hours/offline state (server-evaluated, `no-store`).
|
|
265
|
+
* @param {string} widgetId
|
|
266
|
+
* @returns {Promise<{open:boolean, offlineBehavior:string, offlineFormConfig?:Object}>}
|
|
267
|
+
*/
|
|
268
|
+
async status(widgetId) {
|
|
269
|
+
this.sdk.validateParams(
|
|
270
|
+
{ widgetId },
|
|
271
|
+
{ widgetId: { type: 'string', required: true } },
|
|
272
|
+
);
|
|
273
|
+
return internalRequest(this.sdk, `/webchat/${widgetId}/status`, 'GET', {}, true);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Opt in (or out) of a transcript email for this conversation. Only
|
|
278
|
+
* stores the choice -- the email is sent at conversation close.
|
|
279
|
+
* @param {Object} params
|
|
280
|
+
* @param {string} params.widgetId
|
|
281
|
+
* @param {string} params.token
|
|
282
|
+
* @param {boolean} params.optIn
|
|
283
|
+
* @param {string} [params.email] - Required when `optIn` is true.
|
|
284
|
+
* @returns {Promise<{optIn:boolean, email:?string}>}
|
|
285
|
+
*/
|
|
286
|
+
async transcript({ widgetId, token, optIn, email } = {}) {
|
|
287
|
+
this.sdk.validateParams(
|
|
288
|
+
{ widgetId, token, optIn },
|
|
289
|
+
{
|
|
290
|
+
widgetId: { type: 'string', required: true },
|
|
291
|
+
token: { type: 'string', required: true },
|
|
292
|
+
optIn: { type: 'boolean', required: true },
|
|
293
|
+
},
|
|
294
|
+
);
|
|
295
|
+
return internalRequest(
|
|
296
|
+
this.sdk,
|
|
297
|
+
`/webchat/${widgetId}/transcript`,
|
|
298
|
+
'POST',
|
|
299
|
+
{ body: { optIn, email }, ...authHeaders(token) },
|
|
300
|
+
true,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Post-session-start HMAC identity verification (plan §5/P7 identify
|
|
306
|
+
* pin). Same canonicalization as `session.create`'s `visitorData.hash`:
|
|
307
|
+
* HMAC-SHA256(sorted `key=value&...` over every field except `hash`,
|
|
308
|
+
* identitySecret). Verified fields merge into the conversation's
|
|
309
|
+
* visitorData and (best-effort) link peopleId; an unsigned/mismatched
|
|
310
|
+
* hash is rejected outright, nothing stored.
|
|
311
|
+
* @param {Object} params
|
|
312
|
+
* @param {string} params.widgetId
|
|
313
|
+
* @param {string} params.token - Session JWT.
|
|
314
|
+
* @param {string} [params.userId]
|
|
315
|
+
* @param {string} [params.email]
|
|
316
|
+
* @param {string} [params.name]
|
|
317
|
+
* @param {string} [params.phone]
|
|
318
|
+
* @param {Object} [params.custom]
|
|
319
|
+
* @param {string} params.hash
|
|
320
|
+
* @returns {Promise<Object>}
|
|
321
|
+
*/
|
|
322
|
+
async identify({ widgetId, token, hash, ...fields } = {}) {
|
|
323
|
+
this.sdk.validateParams(
|
|
324
|
+
{ widgetId, token, hash },
|
|
325
|
+
{
|
|
326
|
+
widgetId: { type: 'string', required: true },
|
|
327
|
+
token: { type: 'string', required: true },
|
|
328
|
+
hash: { type: 'string', required: true },
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
return internalRequest(
|
|
332
|
+
this.sdk,
|
|
333
|
+
`/webchat/${widgetId}/identify`,
|
|
334
|
+
'POST',
|
|
335
|
+
{ body: { ...fields, hash }, ...authHeaders(token) },
|
|
336
|
+
true,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Emit a typing signal over an already-connected `/webchat` namespace
|
|
342
|
+
* socket (`webchatSocket.js`/`connectWebchatSocket` precedent -- handshake
|
|
343
|
+
* `auth:{namespace:'webchat', widgetId, token}`). The SDK owns no socket
|
|
344
|
+
* transport (liveQuery.js precedent): pass an already-connected
|
|
345
|
+
* socket.io-client instance in, this is a thin `emit` wrapper, not a
|
|
346
|
+
* connection manager.
|
|
347
|
+
* @param {Object} params
|
|
348
|
+
* @param {Object} params.socket - Connected socket.io-client instance.
|
|
349
|
+
* @param {boolean} [params.isTyping=true]
|
|
350
|
+
*/
|
|
351
|
+
typing({ socket, isTyping = true } = {}) {
|
|
352
|
+
if (!socket || typeof socket.emit !== 'function') {
|
|
353
|
+
throw new Error(
|
|
354
|
+
'webchat.visitor.typing :: a connected socket.io-client instance is required (pass { socket })',
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
socket.emit('webchat.typing', { isTyping });
|
|
358
|
+
}
|
|
359
|
+
}
|
package/services/webchat.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { internalRequest } from '../base.js';
|
|
2
|
+
import { WebchatVisitorService } from './webchat/VisitorService.js';
|
|
2
3
|
|
|
3
4
|
// WebChat P0: agent-facing widget CRUD. Endpoints live under
|
|
4
5
|
// /webchat/widgets/ (checkApiAuth) -- deliberately not bare /webchat/, which
|
|
5
|
-
// is
|
|
6
|
+
// is the (now-implemented, P7) unauthenticated visitor surface -- see
|
|
7
|
+
// `sdk.webchat.visitor` (./webchat/VisitorService.js) (plan §5/§10 Q7).
|
|
6
8
|
export class WebchatWidgetKeysService {
|
|
7
9
|
constructor(sdk) {
|
|
8
10
|
this.sdk = sdk;
|
|
@@ -236,6 +238,51 @@ export class WebchatConversationsService {
|
|
|
236
238
|
this.sdk = sdk;
|
|
237
239
|
this.messages = new WebchatConversationMessagesService(sdk);
|
|
238
240
|
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Send a typing indicator into a webchat conversation as an agent.
|
|
244
|
+
* @param {string} widgetId
|
|
245
|
+
* @param {string} engagementSessionId
|
|
246
|
+
* @param {boolean} isTyping
|
|
247
|
+
* @returns {Promise<Object>}
|
|
248
|
+
*/
|
|
249
|
+
async typing(widgetId, engagementSessionId, isTyping) {
|
|
250
|
+
this.sdk.validateParams(
|
|
251
|
+
{ widgetId, engagementSessionId },
|
|
252
|
+
{
|
|
253
|
+
widgetId: { type: 'string', required: true },
|
|
254
|
+
engagementSessionId: { type: 'string', required: true },
|
|
255
|
+
},
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const params = { body: { isTyping: isTyping !== false } };
|
|
259
|
+
|
|
260
|
+
return internalRequest(
|
|
261
|
+
this.sdk,
|
|
262
|
+
`/webchat/widgets/${widgetId}/conversations/${engagementSessionId}/typing`,
|
|
263
|
+
'POST',
|
|
264
|
+
params,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Look up a webchat conversation by engagementSessionId -- resolves the
|
|
270
|
+
* widgetId + verified-visitor badge data for the agent thread.
|
|
271
|
+
* @param {string} engagementSessionId
|
|
272
|
+
* @returns {Promise<Object>}
|
|
273
|
+
*/
|
|
274
|
+
async get(engagementSessionId) {
|
|
275
|
+
this.sdk.validateParams(
|
|
276
|
+
{ engagementSessionId },
|
|
277
|
+
{ engagementSessionId: { type: 'string', required: true } },
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
return internalRequest(
|
|
281
|
+
this.sdk,
|
|
282
|
+
`/webchat/widgets/conversations/${engagementSessionId}`,
|
|
283
|
+
'GET',
|
|
284
|
+
);
|
|
285
|
+
}
|
|
239
286
|
}
|
|
240
287
|
|
|
241
288
|
export class WebchatService {
|
|
@@ -243,5 +290,9 @@ export class WebchatService {
|
|
|
243
290
|
this.sdk = sdk;
|
|
244
291
|
this.widgets = new WebchatWidgetsService(sdk);
|
|
245
292
|
this.conversations = new WebchatConversationsService(sdk);
|
|
293
|
+
// Public visitor surface (plan §10 Q7, esign.public pattern) -- no
|
|
294
|
+
// agent token required; a custom-UI integration constructs its own sdk
|
|
295
|
+
// instance with just `{namespace}` and uses only this namespace.
|
|
296
|
+
this.visitor = new WebchatVisitorService(sdk);
|
|
246
297
|
}
|
|
247
298
|
}
|