@eshal-bot/chat-widget 0.1.67 → 0.1.68
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/chat-widget.esm.js +2 -2
- package/dist/chat-widget.js +164 -153
- package/dist/chat-widget.min.js +2 -2
- package/dist/chat-widget.umd.js +2 -2
- package/package.json +1 -1
package/dist/chat-widget.js
CHANGED
|
@@ -8010,20 +8010,21 @@
|
|
|
8010
8010
|
* Sends a streaming chat request using fetch
|
|
8011
8011
|
* @param {object} request - Chat request object
|
|
8012
8012
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
8013
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
|
|
8013
8014
|
*/
|
|
8014
|
-
const sendStreamingRequest = async (request, apiBaseUrl,
|
|
8015
|
+
const sendStreamingRequest = async (request, apiBaseUrl, getAuthToken) => {
|
|
8015
8016
|
const headers = {
|
|
8016
8017
|
'Content-Type': 'application/json',
|
|
8017
8018
|
Accept: 'text/event-stream'
|
|
8018
8019
|
};
|
|
8019
8020
|
|
|
8020
|
-
//
|
|
8021
|
-
// Mode B
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8021
|
+
// In-app JWT path: call getAuthToken() and set Authorization header
|
|
8022
|
+
// Mode B proxy: no auth header from widget; proxy injects x-eshal-sig + x-eshal-org server-side
|
|
8023
|
+
if (typeof getAuthToken === 'function') {
|
|
8024
|
+
const token = await getAuthToken();
|
|
8025
|
+
if (token) {
|
|
8026
|
+
headers['Authorization'] = "Bearer ".concat(token);
|
|
8027
|
+
}
|
|
8027
8028
|
}
|
|
8028
8029
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
|
|
8029
8030
|
method: 'POST',
|
|
@@ -8790,17 +8791,17 @@
|
|
|
8790
8791
|
* @param {string} apiBaseUrl - Frontend base URL, e.g. https://demo.eshal.ai
|
|
8791
8792
|
* @returns {string} Voice WS base, e.g. wss://demo.eshal.ai/voice/ws
|
|
8792
8793
|
*/
|
|
8793
|
-
const getWebSocketUrl = apiBaseUrl => {
|
|
8794
|
+
const getWebSocketUrl = (apiBaseUrl, wsBaseUrl) => {
|
|
8795
|
+
// If the backend returned a wsBaseUrl in agentConfig (or customer passed one
|
|
8796
|
+
// explicitly), use it directly — it already points at the correct host.
|
|
8797
|
+
if (wsBaseUrl) return wsBaseUrl.replace(/\/$/, '');
|
|
8794
8798
|
if (!apiBaseUrl) {
|
|
8795
|
-
// No base URL yet (config still loading) — safe public default.
|
|
8796
8799
|
return 'wss://dev.eshal.ai/voice/ws';
|
|
8797
8800
|
}
|
|
8798
8801
|
try {
|
|
8799
8802
|
const url = new URL(apiBaseUrl);
|
|
8800
|
-
// http→ws, https→wss; point at the frontend voice-proxy path.
|
|
8801
8803
|
url.protocol = url.protocol.replace(/^http/, 'ws');
|
|
8802
8804
|
url.pathname = '/voice/ws';
|
|
8803
|
-
// Clean URL without a trailing slash.
|
|
8804
8805
|
return url.toString().replace(/\/$/, '');
|
|
8805
8806
|
} catch (error) {
|
|
8806
8807
|
console.error("Invalid API base URL provided for WebSocket:", error);
|
|
@@ -9144,9 +9145,10 @@
|
|
|
9144
9145
|
welcomeMessage,
|
|
9145
9146
|
quickQuestions = [],
|
|
9146
9147
|
apiBaseUrl,
|
|
9148
|
+
wsBaseUrl,
|
|
9147
9149
|
apiKey,
|
|
9148
9150
|
organizationId,
|
|
9149
|
-
|
|
9151
|
+
getAuthToken,
|
|
9150
9152
|
autoOpen,
|
|
9151
9153
|
openDelay,
|
|
9152
9154
|
darkMode,
|
|
@@ -9314,11 +9316,9 @@
|
|
|
9314
9316
|
const visitorProfileRef = reactExports.useRef(null);
|
|
9315
9317
|
const websocketUrl = reactExports.useMemo(() => {
|
|
9316
9318
|
if (!organizationId) return null;
|
|
9317
|
-
|
|
9318
|
-
// the widget is embedded with) → wss://{host}/voice/ws. No override.
|
|
9319
|
-
const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl);
|
|
9319
|
+
const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl, wsBaseUrl);
|
|
9320
9320
|
return "".concat(resolvedWsBaseUrl, "/").concat(organizationId, "/").concat(bidiSessionId);
|
|
9321
|
-
}, [apiBaseUrl, organizationId, bidiSessionId]);
|
|
9321
|
+
}, [apiBaseUrl, wsBaseUrl, organizationId, bidiSessionId]);
|
|
9322
9322
|
const getNextMessageId = reactExports.useCallback(() => {
|
|
9323
9323
|
const id = "msg-".concat(messageIdRef.current);
|
|
9324
9324
|
messageIdRef.current += 1;
|
|
@@ -9783,7 +9783,7 @@
|
|
|
9783
9783
|
if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
|
|
9784
9784
|
// For onboarding fields, just consume the stream without processing.
|
|
9785
9785
|
// Single attempt — no retry, no user-visible partial state to clean up.
|
|
9786
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9786
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9787
9787
|
const reader = stream.getReader();
|
|
9788
9788
|
const decoder = new TextDecoder();
|
|
9789
9789
|
while (true) {
|
|
@@ -9802,7 +9802,7 @@
|
|
|
9802
9802
|
let lastErr;
|
|
9803
9803
|
for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
9804
9804
|
try {
|
|
9805
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9805
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9806
9806
|
await processStream(stream, setMessages);
|
|
9807
9807
|
updateActivity(organizationId);
|
|
9808
9808
|
return {
|
|
@@ -9830,7 +9830,7 @@
|
|
|
9830
9830
|
|
|
9831
9831
|
// Non-chat, non-onboarding apiType (shouldn't currently happen):
|
|
9832
9832
|
// fire and forget, single attempt.
|
|
9833
|
-
await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9833
|
+
await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9834
9834
|
updateActivity(organizationId);
|
|
9835
9835
|
return {
|
|
9836
9836
|
success: true
|
|
@@ -9866,7 +9866,7 @@
|
|
|
9866
9866
|
// UX-M09: release the concurrency guard so the next send can proceed.
|
|
9867
9867
|
isSendingRef.current = false;
|
|
9868
9868
|
}
|
|
9869
|
-
}, [apiBaseUrl, apiKey, getNextMessageId,
|
|
9869
|
+
}, [apiBaseUrl, apiKey, getNextMessageId, getAuthToken, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
|
|
9870
9870
|
const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
|
|
9871
9871
|
const questions = getOnboardingQuestions(onboardingQuestions);
|
|
9872
9872
|
if (questions.length === 0) return;
|
|
@@ -94384,7 +94384,7 @@
|
|
|
94384
94384
|
let {
|
|
94385
94385
|
sources = [],
|
|
94386
94386
|
apiBaseUrl,
|
|
94387
|
-
|
|
94387
|
+
getAuthToken,
|
|
94388
94388
|
isDark,
|
|
94389
94389
|
textColor,
|
|
94390
94390
|
fontFamily,
|
|
@@ -94410,7 +94410,7 @@
|
|
|
94410
94410
|
// corpora that have none of the above.
|
|
94411
94411
|
const _downloadKey = s => s.url || s.source_id || s.source_name || "";
|
|
94412
94412
|
const handleFileDownload = async source => {
|
|
94413
|
-
if (!apiBaseUrl
|
|
94413
|
+
if (!apiBaseUrl) return;
|
|
94414
94414
|
// SharePoint proxy URLs are GET endpoints that stream the file
|
|
94415
94415
|
// directly via Microsoft Graph using the stored OAuth credential.
|
|
94416
94416
|
// Bypass the legacy POST /file-download lookup (which expects an
|
|
@@ -94436,11 +94436,14 @@
|
|
|
94436
94436
|
// set, or future deployments that set it) pass through
|
|
94437
94437
|
// unchanged.
|
|
94438
94438
|
const spUrl = source.url.startsWith("http") ? source.url : "".concat(apiBaseUrl.replace(/\/$/, "")).concat(source.url);
|
|
94439
|
+
const spHeaders = {};
|
|
94440
|
+
if (typeof getAuthToken === 'function') {
|
|
94441
|
+
const token = await getAuthToken();
|
|
94442
|
+
if (token) spHeaders['Authorization'] = "Bearer ".concat(token);
|
|
94443
|
+
}
|
|
94439
94444
|
res = await fetch(spUrl, {
|
|
94440
94445
|
method: "GET",
|
|
94441
|
-
headers:
|
|
94442
|
-
"x-eshal-org": organizationId
|
|
94443
|
-
},
|
|
94446
|
+
headers: spHeaders,
|
|
94444
94447
|
credentials: "include"
|
|
94445
94448
|
});
|
|
94446
94449
|
} else {
|
|
@@ -94456,15 +94459,18 @@
|
|
|
94456
94459
|
// - source.source_name (the user's original filename)
|
|
94457
94460
|
// Sending both lets the server skip the Milvus resolution
|
|
94458
94461
|
// AND advertise the readable name in Content-Disposition.
|
|
94462
|
+
const dlHeaders = {
|
|
94463
|
+
"Content-Type": "application/json"
|
|
94464
|
+
};
|
|
94465
|
+
if (typeof getAuthToken === 'function') {
|
|
94466
|
+
const token = await getAuthToken();
|
|
94467
|
+
if (token) dlHeaders['Authorization'] = "Bearer ".concat(token);
|
|
94468
|
+
}
|
|
94459
94469
|
res = await fetch(url, {
|
|
94460
94470
|
method: "POST",
|
|
94461
|
-
headers:
|
|
94462
|
-
"Content-Type": "application/json",
|
|
94463
|
-
"x-eshal-org": organizationId
|
|
94464
|
-
},
|
|
94471
|
+
headers: dlHeaders,
|
|
94465
94472
|
credentials: "include",
|
|
94466
94473
|
body: JSON.stringify({
|
|
94467
|
-
org_id: organizationId,
|
|
94468
94474
|
source_id: source.source_id,
|
|
94469
94475
|
file_name: source.url || "",
|
|
94470
94476
|
source_name: source.source_name || ""
|
|
@@ -94649,7 +94655,7 @@
|
|
|
94649
94655
|
conciergeName,
|
|
94650
94656
|
companyName,
|
|
94651
94657
|
apiBaseUrl,
|
|
94652
|
-
|
|
94658
|
+
getAuthToken,
|
|
94653
94659
|
onPromptSuggestionClick,
|
|
94654
94660
|
allowFeedback = true,
|
|
94655
94661
|
provideCitations = true,
|
|
@@ -94821,7 +94827,7 @@
|
|
|
94821
94827
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94822
94828
|
sources: message.sources,
|
|
94823
94829
|
apiBaseUrl: apiBaseUrl,
|
|
94824
|
-
|
|
94830
|
+
getAuthToken: getAuthToken,
|
|
94825
94831
|
isDark: isDark,
|
|
94826
94832
|
textColor: messageTextColor,
|
|
94827
94833
|
fontFamily: messageFontFamily,
|
|
@@ -94847,7 +94853,7 @@
|
|
|
94847
94853
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94848
94854
|
sources: message.sources,
|
|
94849
94855
|
apiBaseUrl: apiBaseUrl,
|
|
94850
|
-
|
|
94856
|
+
getAuthToken: getAuthToken,
|
|
94851
94857
|
isDark: isDark,
|
|
94852
94858
|
textColor: messageTextColor,
|
|
94853
94859
|
fontFamily: messageFontFamily,
|
|
@@ -95027,7 +95033,7 @@
|
|
|
95027
95033
|
conciergeName,
|
|
95028
95034
|
companyName,
|
|
95029
95035
|
apiBaseUrl,
|
|
95030
|
-
|
|
95036
|
+
getAuthToken,
|
|
95031
95037
|
onPromptSuggestionClick,
|
|
95032
95038
|
allowFeedback,
|
|
95033
95039
|
provideCitations,
|
|
@@ -95071,7 +95077,7 @@
|
|
|
95071
95077
|
conciergeName: conciergeName,
|
|
95072
95078
|
companyName: companyName,
|
|
95073
95079
|
apiBaseUrl: apiBaseUrl,
|
|
95074
|
-
|
|
95080
|
+
getAuthToken: getAuthToken,
|
|
95075
95081
|
onPromptSuggestionClick: onPromptSuggestionClick,
|
|
95076
95082
|
allowFeedback: allowFeedback,
|
|
95077
95083
|
provideCitations: provideCitations,
|
|
@@ -95916,7 +95922,7 @@
|
|
|
95916
95922
|
conciergeName,
|
|
95917
95923
|
companyName,
|
|
95918
95924
|
apiBaseUrl,
|
|
95919
|
-
|
|
95925
|
+
getAuthToken,
|
|
95920
95926
|
onPromptSuggestionClick,
|
|
95921
95927
|
conversationResetKey,
|
|
95922
95928
|
allowFeedback,
|
|
@@ -96098,7 +96104,7 @@
|
|
|
96098
96104
|
conciergeName: conciergeName,
|
|
96099
96105
|
companyName: companyName,
|
|
96100
96106
|
apiBaseUrl: apiBaseUrl,
|
|
96101
|
-
|
|
96107
|
+
getAuthToken: getAuthToken,
|
|
96102
96108
|
allowFeedback: allowFeedback,
|
|
96103
96109
|
provideCitations: provideCitations,
|
|
96104
96110
|
onFeedback: onFeedback,
|
|
@@ -96130,7 +96136,7 @@
|
|
|
96130
96136
|
conciergeName: conciergeName,
|
|
96131
96137
|
companyName: companyName,
|
|
96132
96138
|
apiBaseUrl: apiBaseUrl,
|
|
96133
|
-
|
|
96139
|
+
getAuthToken: getAuthToken,
|
|
96134
96140
|
headerTextColor: headerTextColor
|
|
96135
96141
|
}, "skeleton-waiting");
|
|
96136
96142
|
}
|
|
@@ -99645,8 +99651,7 @@
|
|
|
99645
99651
|
showPoweredBy = true,
|
|
99646
99652
|
apiBaseUrl,
|
|
99647
99653
|
apiKey,
|
|
99648
|
-
|
|
99649
|
-
secretKey,
|
|
99654
|
+
getAuthToken,
|
|
99650
99655
|
conversationId,
|
|
99651
99656
|
conciergeId,
|
|
99652
99657
|
onboardingQuestions = [],
|
|
@@ -99794,7 +99799,7 @@
|
|
|
99794
99799
|
setCsatSubmitted(submitted);
|
|
99795
99800
|
if (submitted) setShowCsat(false);
|
|
99796
99801
|
} catch (_unused4) {}
|
|
99797
|
-
}, [conversationId
|
|
99802
|
+
}, [conversationId]);
|
|
99798
99803
|
|
|
99799
99804
|
// Auto-scroll hook
|
|
99800
99805
|
useAutoScroll(messagesContainerRef, [messages, isLoading], {
|
|
@@ -99932,14 +99937,15 @@
|
|
|
99932
99937
|
const handleCsatRating = async (rating, format) => {
|
|
99933
99938
|
setCsatSubmitted(true); // optimistic UI
|
|
99934
99939
|
setShowCsat(false);
|
|
99935
|
-
markCsatSubmitted(
|
|
99940
|
+
markCsatSubmitted(undefined); // persist so page refresh doesn't re-show CSAT
|
|
99936
99941
|
|
|
99937
99942
|
try {
|
|
99938
99943
|
const headers = {
|
|
99939
99944
|
"Content-Type": "application/json"
|
|
99940
99945
|
};
|
|
99941
|
-
if (
|
|
99942
|
-
|
|
99946
|
+
if (typeof getAuthToken === 'function') {
|
|
99947
|
+
const token = await getAuthToken();
|
|
99948
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99943
99949
|
}
|
|
99944
99950
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
99945
99951
|
method: "POST",
|
|
@@ -99949,7 +99955,6 @@
|
|
|
99949
99955
|
conversationId,
|
|
99950
99956
|
rating,
|
|
99951
99957
|
ratingFormat: format,
|
|
99952
|
-
organizationId,
|
|
99953
99958
|
conciergeId
|
|
99954
99959
|
})
|
|
99955
99960
|
});
|
|
@@ -99973,8 +99978,9 @@
|
|
|
99973
99978
|
const headers = {
|
|
99974
99979
|
"Content-Type": "application/json"
|
|
99975
99980
|
};
|
|
99976
|
-
if (
|
|
99977
|
-
|
|
99981
|
+
if (typeof getAuthToken === 'function') {
|
|
99982
|
+
const token = await getAuthToken();
|
|
99983
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99978
99984
|
}
|
|
99979
99985
|
const ratingFormat = normalizeCsatFormat(csatFormat);
|
|
99980
99986
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
@@ -99985,7 +99991,6 @@
|
|
|
99985
99991
|
conversationId,
|
|
99986
99992
|
rating: String(resolveRating),
|
|
99987
99993
|
ratingFormat,
|
|
99988
|
-
organizationId,
|
|
99989
99994
|
conciergeId,
|
|
99990
99995
|
feedback: resolveFeedbackText || undefined
|
|
99991
99996
|
})
|
|
@@ -100019,7 +100024,7 @@
|
|
|
100019
100024
|
} finally {
|
|
100020
100025
|
setIsSubmittingResolve(false);
|
|
100021
100026
|
}
|
|
100022
|
-
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId,
|
|
100027
|
+
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, getAuthToken, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
|
|
100023
100028
|
|
|
100024
100029
|
// Handle X button: first click shows form, second click closes
|
|
100025
100030
|
const handleCloseClick = reactExports.useCallback(() => {
|
|
@@ -100357,7 +100362,7 @@
|
|
|
100357
100362
|
conciergeName: conciergeName,
|
|
100358
100363
|
companyName: companyName,
|
|
100359
100364
|
apiBaseUrl: apiBaseUrl,
|
|
100360
|
-
|
|
100365
|
+
getAuthToken: getAuthToken,
|
|
100361
100366
|
conversationResetKey: conversationId,
|
|
100362
100367
|
allowFeedback: allowFeedback,
|
|
100363
100368
|
provideCitations: provideCitations,
|
|
@@ -100442,8 +100447,7 @@
|
|
|
100442
100447
|
|
|
100443
100448
|
/**
|
|
100444
100449
|
* @param {string|undefined|null} apiBaseUrl
|
|
100445
|
-
* @param {
|
|
100446
|
-
* @param {string|undefined|null} secretKey — Mode A/B: orgId resolved server-side
|
|
100450
|
+
* @param {Function|undefined|null} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined/null (Mode B proxy)
|
|
100447
100451
|
* @returns {{
|
|
100448
100452
|
* categories: Array<{id:string,label:string,count:number,tags:string[]}>,
|
|
100449
100453
|
* loading: boolean,
|
|
@@ -100451,17 +100455,15 @@
|
|
|
100451
100455
|
* hasFilters: boolean,
|
|
100452
100456
|
* }}
|
|
100453
100457
|
*/
|
|
100454
|
-
function useFilterCatalog(apiBaseUrl,
|
|
100455
|
-
|
|
100456
|
-
// Mode C: orgId present (legacy) → orgId-in-path endpoint
|
|
100457
|
-
const hasAuth = !!(apiBaseUrl && (orgId || secretKey));
|
|
100458
|
+
function useFilterCatalog(apiBaseUrl, getAuthToken) {
|
|
100459
|
+
const hasAuth = !!apiBaseUrl;
|
|
100458
100460
|
const [state, setState] = reactExports.useState({
|
|
100459
100461
|
categories: [],
|
|
100460
100462
|
loading: hasAuth,
|
|
100461
100463
|
error: null
|
|
100462
100464
|
});
|
|
100463
100465
|
reactExports.useEffect(() => {
|
|
100464
|
-
if (!apiBaseUrl
|
|
100466
|
+
if (!apiBaseUrl) {
|
|
100465
100467
|
setState({
|
|
100466
100468
|
categories: [],
|
|
100467
100469
|
loading: false,
|
|
@@ -100469,7 +100471,7 @@
|
|
|
100469
100471
|
});
|
|
100470
100472
|
return;
|
|
100471
100473
|
}
|
|
100472
|
-
const cacheKey =
|
|
100474
|
+
const cacheKey = getAuthToken ? "".concat(apiBaseUrl, "|__session__") : "".concat(apiBaseUrl, "|__proxy__");
|
|
100473
100475
|
const cached = _PROCESS_CACHE.get(cacheKey);
|
|
100474
100476
|
if (cached) {
|
|
100475
100477
|
setState({
|
|
@@ -100485,45 +100487,54 @@
|
|
|
100485
100487
|
error: null
|
|
100486
100488
|
}));
|
|
100487
100489
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100488
|
-
const url =
|
|
100489
|
-
|
|
100490
|
-
|
|
100491
|
-
|
|
100492
|
-
|
|
100493
|
-
|
|
100494
|
-
|
|
100495
|
-
|
|
100496
|
-
|
|
100497
|
-
|
|
100498
|
-
|
|
100490
|
+
const url = "".concat(base, "/api/v1/widget/filters");
|
|
100491
|
+
(async () => {
|
|
100492
|
+
try {
|
|
100493
|
+
const headers = {
|
|
100494
|
+
Accept: "application/json"
|
|
100495
|
+
};
|
|
100496
|
+
// In-app JWT path: set Authorization header
|
|
100497
|
+
// Mode B proxy: no auth header; proxy handles org resolution
|
|
100498
|
+
if (typeof getAuthToken === 'function') {
|
|
100499
|
+
const token = await getAuthToken();
|
|
100500
|
+
if (token) {
|
|
100501
|
+
headers['Authorization'] = "Bearer ".concat(token);
|
|
100502
|
+
}
|
|
100503
|
+
}
|
|
100504
|
+
const res = await fetch(url, {
|
|
100505
|
+
method: "GET",
|
|
100506
|
+
headers
|
|
100507
|
+
});
|
|
100508
|
+
if (!res.ok) {
|
|
100509
|
+
throw new Error("filter catalog HTTP ".concat(res.status));
|
|
100510
|
+
}
|
|
100511
|
+
const body = await res.json();
|
|
100512
|
+
const categories = Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100513
|
+
if (cancelled) return;
|
|
100514
|
+
_PROCESS_CACHE.set(cacheKey, categories);
|
|
100515
|
+
setState({
|
|
100516
|
+
categories,
|
|
100517
|
+
loading: false,
|
|
100518
|
+
error: null
|
|
100519
|
+
});
|
|
100520
|
+
} catch (err) {
|
|
100521
|
+
if (cancelled) return;
|
|
100522
|
+
// Fail-soft: log and present empty so the UI hides itself.
|
|
100523
|
+
// We do NOT bubble the error to the caller because chat
|
|
100524
|
+
// should keep working even if the catalog is unreachable.
|
|
100525
|
+
// eslint-disable-next-line no-console
|
|
100526
|
+
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100527
|
+
setState({
|
|
100528
|
+
categories: [],
|
|
100529
|
+
loading: false,
|
|
100530
|
+
error: err
|
|
100531
|
+
});
|
|
100499
100532
|
}
|
|
100500
|
-
|
|
100501
|
-
return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100502
|
-
}).then(categories => {
|
|
100503
|
-
if (cancelled) return;
|
|
100504
|
-
_PROCESS_CACHE.set(cacheKey, categories);
|
|
100505
|
-
setState({
|
|
100506
|
-
categories,
|
|
100507
|
-
loading: false,
|
|
100508
|
-
error: null
|
|
100509
|
-
});
|
|
100510
|
-
}).catch(err => {
|
|
100511
|
-
if (cancelled) return;
|
|
100512
|
-
// Fail-soft: log and present empty so the UI hides itself.
|
|
100513
|
-
// We do NOT bubble the error to the caller because chat
|
|
100514
|
-
// should keep working even if the catalog is unreachable.
|
|
100515
|
-
// eslint-disable-next-line no-console
|
|
100516
|
-
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100517
|
-
setState({
|
|
100518
|
-
categories: [],
|
|
100519
|
-
loading: false,
|
|
100520
|
-
error: err
|
|
100521
|
-
});
|
|
100522
|
-
});
|
|
100533
|
+
})();
|
|
100523
100534
|
return () => {
|
|
100524
100535
|
cancelled = true;
|
|
100525
100536
|
};
|
|
100526
|
-
}, [apiBaseUrl,
|
|
100537
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100527
100538
|
const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
|
|
100528
100539
|
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
100529
100540
|
hasFilters
|
|
@@ -100602,24 +100613,28 @@
|
|
|
100602
100613
|
/**
|
|
100603
100614
|
* Fetches agent configuration from the deploy-agent endpoint
|
|
100604
100615
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
100605
|
-
* @param {
|
|
100616
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
|
|
100606
100617
|
* @returns {Promise<object>} Agent configuration object
|
|
100607
100618
|
*/
|
|
100608
|
-
const fetchAgentConfig = async (apiBaseUrl,
|
|
100619
|
+
const fetchAgentConfig = async (apiBaseUrl, getAuthToken) => {
|
|
100609
100620
|
if (!apiBaseUrl) {
|
|
100610
100621
|
throw new Error("apiBaseUrl is required");
|
|
100611
100622
|
}
|
|
100612
|
-
// Mode A: secretKey in header, no orgId in URL
|
|
100613
|
-
// Mode B (proxy): no secretKey, no orgId — proxy intercepts and signs the request
|
|
100614
|
-
// Mode C legacy: orgId in URL path
|
|
100615
|
-
|
|
100616
100623
|
try {
|
|
100617
100624
|
const base = apiBaseUrl.replace(/\/$/, '');
|
|
100618
|
-
const url =
|
|
100625
|
+
const url = "".concat(base, "/api/v1/deploy-agent");
|
|
100619
100626
|
const headers = {
|
|
100620
100627
|
"Content-Type": "application/json"
|
|
100621
100628
|
};
|
|
100622
|
-
|
|
100629
|
+
|
|
100630
|
+
// In-app JWT path: set Authorization header
|
|
100631
|
+
// Mode B proxy: no auth header; proxy handles org resolution
|
|
100632
|
+
if (typeof getAuthToken === 'function') {
|
|
100633
|
+
const token = await getAuthToken();
|
|
100634
|
+
if (token) {
|
|
100635
|
+
headers['Authorization'] = "Bearer ".concat(token);
|
|
100636
|
+
}
|
|
100637
|
+
}
|
|
100623
100638
|
const response = await fetch(url, {
|
|
100624
100639
|
method: "GET",
|
|
100625
100640
|
headers,
|
|
@@ -100639,11 +100654,10 @@
|
|
|
100639
100654
|
const ChatWidget = _ref => {
|
|
100640
100655
|
var _agentConfig$concierg;
|
|
100641
100656
|
let {
|
|
100642
|
-
|
|
100643
|
-
|
|
100644
|
-
|
|
100645
|
-
|
|
100646
|
-
// legacy — kept for backward compatibility
|
|
100657
|
+
/**
|
|
100658
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Keycloak Bearer token (in-app JWT path)
|
|
100659
|
+
*/
|
|
100660
|
+
getAuthToken,
|
|
100647
100661
|
apiBaseUrl,
|
|
100648
100662
|
// Optional override props (will override fetched config if provided)
|
|
100649
100663
|
darkMode,
|
|
@@ -100699,13 +100713,9 @@
|
|
|
100699
100713
|
setIsLoadingConfig(false);
|
|
100700
100714
|
return;
|
|
100701
100715
|
}
|
|
100702
|
-
// Mode B proxy: neither secretKey nor orgId in browser — proxy handles auth
|
|
100703
|
-
|
|
100704
100716
|
try {
|
|
100705
100717
|
setIsLoadingConfig(true);
|
|
100706
|
-
|
|
100707
|
-
// header and omit the orgId path segment. Pass orgId for Mode C legacy.
|
|
100708
|
-
const config = await fetchAgentConfig(apiBaseUrl, orgId, secretKey);
|
|
100718
|
+
const config = await fetchAgentConfig(apiBaseUrl, getAuthToken);
|
|
100709
100719
|
setAgentConfig(config);
|
|
100710
100720
|
setConfigError(null);
|
|
100711
100721
|
} catch (error) {
|
|
@@ -100716,11 +100726,11 @@
|
|
|
100716
100726
|
}
|
|
100717
100727
|
};
|
|
100718
100728
|
loadAgentConfig();
|
|
100719
|
-
}, [
|
|
100729
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100720
100730
|
|
|
100721
100731
|
// Map API response to widget props (use props as overrides if provided)
|
|
100722
100732
|
const widgetConfig = reactExports.useMemo(() => {
|
|
100723
|
-
var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _typography$headerTex, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita;
|
|
100733
|
+
var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _typography$headerTex, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita, _agentConfig$organiza, _agentConfig$wsBaseUr;
|
|
100724
100734
|
if (!agentConfig) {
|
|
100725
100735
|
return null;
|
|
100726
100736
|
}
|
|
@@ -100779,7 +100789,6 @@
|
|
|
100779
100789
|
fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
|
|
100780
100790
|
fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
|
|
100781
100791
|
defaultLanguage: locale,
|
|
100782
|
-
organizationId: orgId,
|
|
100783
100792
|
headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
|
|
100784
100793
|
headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
|
|
100785
100794
|
headerTextColor: (_typography$headerTex = typography.headerTextColor) !== null && _typography$headerTex !== void 0 ? _typography$headerTex : "#FFFFFF",
|
|
@@ -100816,9 +100825,13 @@
|
|
|
100816
100825
|
// every message and tell the backend to skip citation generation.
|
|
100817
100826
|
// Defaults to true so agents that haven't set the field keep showing
|
|
100818
100827
|
// citations (matches the testing-lab / chatbot-preview default).
|
|
100819
|
-
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
|
|
100828
|
+
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true,
|
|
100829
|
+
// organizationId from the backend response — used internally for
|
|
100830
|
+
// session isolation (localStorage keys) and WS paths. Not a user prop.
|
|
100831
|
+
organizationId: (_agentConfig$organiza = agentConfig.organizationId) !== null && _agentConfig$organiza !== void 0 ? _agentConfig$organiza : null,
|
|
100832
|
+
wsBaseUrl: (_agentConfig$wsBaseUr = agentConfig.wsBaseUrl) !== null && _agentConfig$wsBaseUr !== void 0 ? _agentConfig$wsBaseUr : null
|
|
100820
100833
|
};
|
|
100821
|
-
}, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage,
|
|
100834
|
+
}, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage, headerTextBold, headerTextItalic, enableVoiceInteraction, disclaimerEnabled, disclaimerText, disclaimerPosition, disclaimerLinkLabel, disclaimerLinkUrl, showPoweredBy, onboardingQuestions, onboardingEnabled, collectionPrompt, allowedDomains, agentMessageBubbleFontFamily, agentMessageBubbleFontSize, userMessageBubbleFontFamily, userMessageBubbleFontSize
|
|
100822
100835
|
// CSAT dependencies
|
|
100823
100836
|
// (kept minimal because these come from agentConfig)
|
|
100824
100837
|
]);
|
|
@@ -100837,7 +100850,7 @@
|
|
|
100837
100850
|
quickQuestions: [],
|
|
100838
100851
|
apiBaseUrl: apiBaseUrl || "",
|
|
100839
100852
|
apiKey: apiKey || "",
|
|
100840
|
-
|
|
100853
|
+
getAuthToken,
|
|
100841
100854
|
autoOpen: false,
|
|
100842
100855
|
openDelay: 0,
|
|
100843
100856
|
darkMode: false,
|
|
@@ -100845,7 +100858,7 @@
|
|
|
100845
100858
|
onboardingQuestions: [],
|
|
100846
100859
|
onboardingEnabled: false,
|
|
100847
100860
|
collectionPrompt: undefined
|
|
100848
|
-
}), [apiBaseUrl, apiKey,
|
|
100861
|
+
}), [apiBaseUrl, apiKey, getAuthToken]);
|
|
100849
100862
|
|
|
100850
100863
|
// ALWAYS call hooks before any early returns (Rules of Hooks)
|
|
100851
100864
|
const {
|
|
@@ -100887,14 +100900,10 @@
|
|
|
100887
100900
|
welcomeMessage: widgetConfig.welcomeMessage,
|
|
100888
100901
|
quickQuestions: widgetConfig.quickQuestions,
|
|
100889
100902
|
apiBaseUrl,
|
|
100890
|
-
|
|
100891
|
-
// useChatState (→ wss://{host}/voice/ws, the frontend voice proxy).
|
|
100892
|
-
// Nothing is forwarded/overridable here, so a stale backend or embed
|
|
100893
|
-
// wsBaseUrl can't point voice at the old (now non-public)
|
|
100894
|
-
// knowledge-api LoadBalancer.
|
|
100903
|
+
wsBaseUrl: widgetConfig.wsBaseUrl,
|
|
100895
100904
|
apiKey,
|
|
100896
100905
|
organizationId: widgetConfig.organizationId,
|
|
100897
|
-
|
|
100906
|
+
getAuthToken,
|
|
100898
100907
|
autoOpen,
|
|
100899
100908
|
openDelay,
|
|
100900
100909
|
darkMode: widgetConfig.darkMode,
|
|
@@ -100921,25 +100930,33 @@
|
|
|
100921
100930
|
// navigation (but not a page refresh — feedback is not written to
|
|
100922
100931
|
// localStorage so it resets on reload as intended).
|
|
100923
100932
|
updateMessageFeedback(messageContent, feedback);
|
|
100924
|
-
if (!
|
|
100933
|
+
if (!conversationId || !apiBaseUrl) return;
|
|
100925
100934
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100926
|
-
|
|
100927
|
-
|
|
100928
|
-
|
|
100929
|
-
|
|
100930
|
-
|
|
100931
|
-
|
|
100932
|
-
|
|
100933
|
-
|
|
100934
|
-
|
|
100935
|
-
|
|
100936
|
-
|
|
100937
|
-
|
|
100938
|
-
|
|
100939
|
-
|
|
100940
|
-
|
|
100941
|
-
|
|
100942
|
-
|
|
100935
|
+
(async () => {
|
|
100936
|
+
try {
|
|
100937
|
+
const feedbackHeaders = {
|
|
100938
|
+
"Content-Type": "application/json"
|
|
100939
|
+
};
|
|
100940
|
+
if (typeof getAuthToken === 'function') {
|
|
100941
|
+
const token = await getAuthToken();
|
|
100942
|
+
if (token) feedbackHeaders['Authorization'] = "Bearer ".concat(token);
|
|
100943
|
+
}
|
|
100944
|
+
await fetch("".concat(base, "/api/v1/support/feedback"), {
|
|
100945
|
+
method: "POST",
|
|
100946
|
+
headers: feedbackHeaders,
|
|
100947
|
+
credentials: "include",
|
|
100948
|
+
body: JSON.stringify({
|
|
100949
|
+
messageContent,
|
|
100950
|
+
conversationId,
|
|
100951
|
+
feedback,
|
|
100952
|
+
feedbackText
|
|
100953
|
+
})
|
|
100954
|
+
});
|
|
100955
|
+
} catch (err) {
|
|
100956
|
+
console.error("Feedback submission error:", err);
|
|
100957
|
+
}
|
|
100958
|
+
})();
|
|
100959
|
+
}, [conversationId, apiBaseUrl, getAuthToken, updateMessageFeedback]);
|
|
100943
100960
|
|
|
100944
100961
|
// Refresh chat handler — reset conversation
|
|
100945
100962
|
const handleRefreshChat = reactExports.useCallback(() => {
|
|
@@ -100955,7 +100972,7 @@
|
|
|
100955
100972
|
const {
|
|
100956
100973
|
categories: filterCategories,
|
|
100957
100974
|
hasFilters: showFilterStep
|
|
100958
|
-
} = useFilterCatalog(apiBaseUrl,
|
|
100975
|
+
} = useFilterCatalog(apiBaseUrl, getAuthToken);
|
|
100959
100976
|
|
|
100960
100977
|
// Copy chat handler — copy all messages to clipboard
|
|
100961
100978
|
const handleCopyChat = reactExports.useCallback(() => {
|
|
@@ -101060,8 +101077,7 @@
|
|
|
101060
101077
|
showPoweredBy: widgetConfig.showPoweredBy,
|
|
101061
101078
|
apiBaseUrl: apiBaseUrl,
|
|
101062
101079
|
apiKey: apiKey,
|
|
101063
|
-
|
|
101064
|
-
secretKey: secretKey,
|
|
101080
|
+
getAuthToken: getAuthToken,
|
|
101065
101081
|
conversationId: conversationId,
|
|
101066
101082
|
conciergeId: (_agentConfig$concierg = agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.conciergeId) !== null && _agentConfig$concierg !== void 0 ? _agentConfig$concierg : agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.id,
|
|
101067
101083
|
onboardingQuestions: widgetConfig.onboardingQuestions,
|
|
@@ -101209,15 +101225,10 @@
|
|
|
101209
101225
|
}
|
|
101210
101226
|
|
|
101211
101227
|
// Render component
|
|
101212
|
-
// Mode
|
|
101213
|
-
//
|
|
101214
|
-
// Both: apiBaseUrl required
|
|
101215
|
-
// Mode B proxy: apiBaseUrl alone is enough — no secretKey or orgId in browser
|
|
101228
|
+
// Mode B proxy: apiBaseUrl alone is enough — no key or orgId in browser
|
|
101229
|
+
// In-app JWT: getAuthToken fn passed via config
|
|
101216
101230
|
const hasNewAuth = config.apiBaseUrl;
|
|
101217
101231
|
const widgetProps = hasNewAuth ? _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
|
|
101218
|
-
// Auth props — secretKey replaces orgId in Mode A/B
|
|
101219
|
-
secretKey: config.secretKey || null,
|
|
101220
|
-
orgId: config.orgId || null,
|
|
101221
101232
|
apiBaseUrl: config.apiBaseUrl
|
|
101222
101233
|
}, options.wsBaseUrl !== undefined && {
|
|
101223
101234
|
wsBaseUrl: config.wsBaseUrl
|