@eshal-bot/chat-widget 0.1.61 → 0.1.63
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 +3 -3
- package/dist/chat-widget.js +162 -50
- package/dist/chat-widget.min.js +3 -3
- package/dist/chat-widget.umd.js +3 -3
- package/package.json +93 -93
package/dist/chat-widget.js
CHANGED
|
@@ -8011,14 +8011,18 @@
|
|
|
8011
8011
|
* @param {object} request - Chat request object
|
|
8012
8012
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
8013
8013
|
*/
|
|
8014
|
-
const sendStreamingRequest = async (request, apiBaseUrl, organizationId) => {
|
|
8014
|
+
const sendStreamingRequest = async (request, apiBaseUrl, organizationId, secretKey) => {
|
|
8015
8015
|
const headers = {
|
|
8016
8016
|
'Content-Type': 'application/json',
|
|
8017
8017
|
Accept: 'text/event-stream'
|
|
8018
8018
|
};
|
|
8019
8019
|
|
|
8020
|
-
//
|
|
8021
|
-
|
|
8020
|
+
// Mode A — secret key (direct embed): key replaces orgId in the header
|
|
8021
|
+
// Mode B — proxy (@eshal-bot/server): no header from widget; proxy injects x-eshal-sig + x-eshal-org
|
|
8022
|
+
// Mode C — legacy: orgId sent directly
|
|
8023
|
+
if (secretKey) {
|
|
8024
|
+
headers['x-eshal-key'] = secretKey;
|
|
8025
|
+
} else if (organizationId) {
|
|
8022
8026
|
headers['x-eshal-org'] = organizationId;
|
|
8023
8027
|
}
|
|
8024
8028
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
|
|
@@ -9129,6 +9133,7 @@
|
|
|
9129
9133
|
apiBaseUrl,
|
|
9130
9134
|
apiKey,
|
|
9131
9135
|
organizationId,
|
|
9136
|
+
secretKey,
|
|
9132
9137
|
autoOpen,
|
|
9133
9138
|
openDelay,
|
|
9134
9139
|
darkMode,
|
|
@@ -9765,7 +9770,7 @@
|
|
|
9765
9770
|
if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
|
|
9766
9771
|
// For onboarding fields, just consume the stream without processing.
|
|
9767
9772
|
// Single attempt — no retry, no user-visible partial state to clean up.
|
|
9768
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
|
|
9773
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId, secretKey);
|
|
9769
9774
|
const reader = stream.getReader();
|
|
9770
9775
|
const decoder = new TextDecoder();
|
|
9771
9776
|
while (true) {
|
|
@@ -9784,7 +9789,7 @@
|
|
|
9784
9789
|
let lastErr;
|
|
9785
9790
|
for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
9786
9791
|
try {
|
|
9787
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
|
|
9792
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId, secretKey);
|
|
9788
9793
|
await processStream(stream, setMessages);
|
|
9789
9794
|
updateActivity(organizationId);
|
|
9790
9795
|
return {
|
|
@@ -91749,6 +91754,31 @@
|
|
|
91749
91754
|
return String(n);
|
|
91750
91755
|
}
|
|
91751
91756
|
|
|
91757
|
+
// ── Currency-aware value formatting ──────────────────────────────────────
|
|
91758
|
+
// A2UI chart data is normalized to `{ label, value }` by A2uiBlock's
|
|
91759
|
+
// numericData(), which drops the original Y-column key. A2uiBlock now threads
|
|
91760
|
+
// that key through as `props.yKey` so these renderers can decide whether the
|
|
91761
|
+
// axis / tooltip values are money. Heuristic mirrors the frontend's
|
|
91762
|
+
// chartShared.tsx `isCurrencyKey` — keep the keyword list in lockstep.
|
|
91763
|
+
// `(^|[_\W])` (not `\b`) so "average_salary" / "total_expenses" still match —
|
|
91764
|
+
// JS `\b` doesn't fire next to `_`.
|
|
91765
|
+
const CURRENCY_KEY_RE$1 = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
|
|
91766
|
+
function isCurrencyKey$1(key) {
|
|
91767
|
+
return typeof key === 'string' && CURRENCY_KEY_RE$1.test(key);
|
|
91768
|
+
}
|
|
91769
|
+
|
|
91770
|
+
// Compact currency for axis ticks → "$120.0k". Full currency for tooltips →
|
|
91771
|
+
// "$87,600" (comma-grouped, up to 2 decimals, no forced trailing zeros so a
|
|
91772
|
+
// whole-dollar salary reads "$81,200" not "$81,200.00").
|
|
91773
|
+
function formatCurrencyCompact(v) {
|
|
91774
|
+
return "$".concat(formatCompactNumber(v));
|
|
91775
|
+
}
|
|
91776
|
+
function formatCurrencyFull(v) {
|
|
91777
|
+
return "$".concat(Number(v).toLocaleString('en-US', {
|
|
91778
|
+
maximumFractionDigits: 2
|
|
91779
|
+
}));
|
|
91780
|
+
}
|
|
91781
|
+
|
|
91752
91782
|
/**
|
|
91753
91783
|
* Recharts bar chart — mirrors agent-sdk's eshalRenderers.BarChart.
|
|
91754
91784
|
*
|
|
@@ -91761,6 +91791,11 @@
|
|
|
91761
91791
|
isDark = false
|
|
91762
91792
|
} = _ref;
|
|
91763
91793
|
const data = Array.isArray(props.data) ? props.data : [];
|
|
91794
|
+
// Whether the Y values are money — drives the `$` on axis ticks + tooltip.
|
|
91795
|
+
const currency = isCurrencyKey$1(props.yKey);
|
|
91796
|
+
// Human series name for the tooltip ("Average Salary"), so it no longer
|
|
91797
|
+
// reads the generic dataKey "value".
|
|
91798
|
+
const seriesName = props.valueLabel || undefined;
|
|
91764
91799
|
const borderColor = isDark ? '#374151' : '#e2e8f0';
|
|
91765
91800
|
const bgColor = isDark ? '#1f2937' : '#ffffff';
|
|
91766
91801
|
const labelColor = isDark ? '#9ca3af' : '#64748b';
|
|
@@ -91835,6 +91870,9 @@
|
|
|
91835
91870
|
fontSize: 11,
|
|
91836
91871
|
fill: labelColor
|
|
91837
91872
|
},
|
|
91873
|
+
angle: 0,
|
|
91874
|
+
textAnchor: "middle",
|
|
91875
|
+
interval: 0,
|
|
91838
91876
|
label: props.xAxisLabel ? {
|
|
91839
91877
|
value: props.xAxisLabel,
|
|
91840
91878
|
position: 'insideBottom',
|
|
@@ -91855,9 +91893,9 @@
|
|
|
91855
91893
|
fontSize: 11,
|
|
91856
91894
|
fill: labelColor
|
|
91857
91895
|
} : undefined,
|
|
91858
|
-
tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
|
|
91896
|
+
tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
|
|
91859
91897
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
|
|
91860
|
-
formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
|
|
91898
|
+
formatter: v => currency ? formatCurrencyFull(v) : props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
|
|
91861
91899
|
contentStyle: {
|
|
91862
91900
|
background: tooltipBg,
|
|
91863
91901
|
border: "1px solid ".concat(borderColor),
|
|
@@ -91867,6 +91905,7 @@
|
|
|
91867
91905
|
}
|
|
91868
91906
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Bar, {
|
|
91869
91907
|
dataKey: "value",
|
|
91908
|
+
name: seriesName,
|
|
91870
91909
|
fill: "#3b82f6",
|
|
91871
91910
|
radius: [2, 2, 0, 0]
|
|
91872
91911
|
})]
|
|
@@ -91950,6 +91989,7 @@
|
|
|
91950
91989
|
donut = false
|
|
91951
91990
|
} = _ref3;
|
|
91952
91991
|
const data = Array.isArray(props.data) ? props.data : [];
|
|
91992
|
+
const currency = isCurrencyKey$1(props.yKey);
|
|
91953
91993
|
const borderColor = isDark ? '#374151' : '#e2e8f0';
|
|
91954
91994
|
const bgColor = isDark ? '#1f2937' : '#ffffff';
|
|
91955
91995
|
const titleColor = isDark ? '#f3f4f6' : '#0f172a';
|
|
@@ -92000,7 +92040,7 @@
|
|
|
92000
92040
|
fill: PIE_COLORS[i % PIE_COLORS.length]
|
|
92001
92041
|
}, i))
|
|
92002
92042
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
|
|
92003
|
-
formatter: v => Number(v).toLocaleString(),
|
|
92043
|
+
formatter: v => currency ? formatCurrencyFull(v) : Number(v).toLocaleString(),
|
|
92004
92044
|
contentStyle: {
|
|
92005
92045
|
background: tooltipBg,
|
|
92006
92046
|
border: "1px solid ".concat(borderColor),
|
|
@@ -92034,6 +92074,8 @@
|
|
|
92034
92074
|
area = false
|
|
92035
92075
|
} = _ref4;
|
|
92036
92076
|
const data = Array.isArray(props.data) ? props.data : [];
|
|
92077
|
+
const currency = isCurrencyKey$1(props.yKey);
|
|
92078
|
+
const seriesName = props.valueLabel || undefined;
|
|
92037
92079
|
const borderColor = isDark ? '#374151' : '#e2e8f0';
|
|
92038
92080
|
const bgColor = isDark ? '#1f2937' : '#ffffff';
|
|
92039
92081
|
const labelColor = isDark ? '#9ca3af' : '#64748b';
|
|
@@ -92109,6 +92151,9 @@
|
|
|
92109
92151
|
fontSize: 11,
|
|
92110
92152
|
fill: labelColor
|
|
92111
92153
|
},
|
|
92154
|
+
angle: 0,
|
|
92155
|
+
textAnchor: "middle",
|
|
92156
|
+
interval: 0,
|
|
92112
92157
|
label: props.xAxisLabel ? {
|
|
92113
92158
|
value: props.xAxisLabel,
|
|
92114
92159
|
position: 'insideBottom',
|
|
@@ -92122,9 +92167,9 @@
|
|
|
92122
92167
|
fontSize: 11,
|
|
92123
92168
|
fill: labelColor
|
|
92124
92169
|
},
|
|
92125
|
-
tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
|
|
92170
|
+
tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
|
|
92126
92171
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
|
|
92127
|
-
formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
|
|
92172
|
+
formatter: v => currency ? formatCurrencyFull(v) : props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
|
|
92128
92173
|
contentStyle: {
|
|
92129
92174
|
background: tooltipBg,
|
|
92130
92175
|
border: "1px solid ".concat(borderColor),
|
|
@@ -92135,6 +92180,7 @@
|
|
|
92135
92180
|
}), area ? /*#__PURE__*/jsxRuntimeExports.jsx(Area, {
|
|
92136
92181
|
type: "monotone",
|
|
92137
92182
|
dataKey: "value",
|
|
92183
|
+
name: seriesName,
|
|
92138
92184
|
stroke: "#3b82f6",
|
|
92139
92185
|
fill: "#3b82f6",
|
|
92140
92186
|
fillOpacity: 0.15,
|
|
@@ -92146,6 +92192,7 @@
|
|
|
92146
92192
|
}) : /*#__PURE__*/jsxRuntimeExports.jsx(Line, {
|
|
92147
92193
|
type: "monotone",
|
|
92148
92194
|
dataKey: "value",
|
|
92195
|
+
name: seriesName,
|
|
92149
92196
|
stroke: "#3b82f6",
|
|
92150
92197
|
strokeWidth: 2,
|
|
92151
92198
|
dot: {
|
|
@@ -92343,7 +92390,14 @@
|
|
|
92343
92390
|
data: rows,
|
|
92344
92391
|
unit: spec.unit,
|
|
92345
92392
|
xAxisLabel: spec.xAxisLabel,
|
|
92346
|
-
yAxisLabel: spec.yAxisLabel
|
|
92393
|
+
yAxisLabel: spec.yAxisLabel,
|
|
92394
|
+
// numericData() collapses the rows to { label, value }, dropping the
|
|
92395
|
+
// original Y-column key. Pass it through so the renderer can detect a
|
|
92396
|
+
// currency column (→ "$" on the Y-axis + tooltip) and label the tooltip
|
|
92397
|
+
// series with the human column name ("Average Salary") instead of the
|
|
92398
|
+
// generic dataKey "value".
|
|
92399
|
+
yKey: y,
|
|
92400
|
+
valueLabel: prettifyLabel(y)
|
|
92347
92401
|
};
|
|
92348
92402
|
if (t === 'pie') return /*#__PURE__*/jsxRuntimeExports.jsx(PieChartRenderer, {
|
|
92349
92403
|
props: chartProps,
|
|
@@ -92670,10 +92724,12 @@
|
|
|
92670
92724
|
|
|
92671
92725
|
// ISO date / datetime prefix — matches the typical SQL output the
|
|
92672
92726
|
// agent's tools produce when GROUP BY-ing by month
|
|
92673
|
-
// (`DATE_TRUNC('month', col)` → `YYYY-MM-DD`)
|
|
92674
|
-
// below so a chart of monthly
|
|
92675
|
-
//
|
|
92676
|
-
|
|
92727
|
+
// (`DATE_TRUNC('month', col)` → `YYYY-MM-DD`, or `to_char(..,'YYYY-MM')`
|
|
92728
|
+
// → `YYYY-MM`). Used by formatAxisTick below so a chart of monthly
|
|
92729
|
+
// expenses shows "Jan '26" / "Feb '26" horizontally instead of
|
|
92730
|
+
// "2026 01 01" or "2026 01" slanted (EV-1187 follow-up + 2026-06-24
|
|
92731
|
+
// day-less variant fix).
|
|
92732
|
+
const ISO_DATE_RE = /^(\d{4})-(\d{2})(?:-\d{2})?(?:[T\s]|$)/;
|
|
92677
92733
|
function formatIsoDateAsMonthTick(value) {
|
|
92678
92734
|
if (typeof value !== 'string') return null;
|
|
92679
92735
|
const m = value.match(ISO_DATE_RE);
|
|
@@ -92716,6 +92772,14 @@
|
|
|
92716
92772
|
if (!SNAKE_CASE_CATEGORY_RE.test(value)) return value;
|
|
92717
92773
|
return prettifyLabel(value);
|
|
92718
92774
|
}
|
|
92775
|
+
|
|
92776
|
+
// Numeric string under a money column, e.g. "81,200" / "105444" / "-1,250.50".
|
|
92777
|
+
// Markdown tables (the common concierge path) arrive as text cells, so a
|
|
92778
|
+
// salary value is the STRING "81,200" — not a number — and skips the
|
|
92779
|
+
// USD_FORMATTER branch below. We prefix "$" while keeping the source's own
|
|
92780
|
+
// grouping/decimals so it reads "$81,200" (not "$81,200.00"). A leading "$"
|
|
92781
|
+
// already present makes the regex miss, so we never double-prefix.
|
|
92782
|
+
const _NUMERIC_STRING_RE = /^-?\d[\d,]*(\.\d+)?$/;
|
|
92719
92783
|
function formatCell(v, columnKey) {
|
|
92720
92784
|
if (v == null) return '';
|
|
92721
92785
|
if (typeof v === 'number') {
|
|
@@ -92725,6 +92789,11 @@
|
|
|
92725
92789
|
return Number.isInteger(v) ? String(v) : v.toFixed(2);
|
|
92726
92790
|
}
|
|
92727
92791
|
if (typeof v === 'object') return JSON.stringify(v);
|
|
92792
|
+
// Numeric string under a money-named column → prefix "$" (Bug: concierge
|
|
92793
|
+
// salary table showed "81,200" with no currency symbol).
|
|
92794
|
+
if (isCurrencyKey(columnKey) && _NUMERIC_STRING_RE.test(String(v).trim())) {
|
|
92795
|
+
return "$".concat(String(v).trim());
|
|
92796
|
+
}
|
|
92728
92797
|
// String cells: prettify snake_case category labels (e.g.
|
|
92729
92798
|
// `office_supplies` → "Office Supplies") so the table reads
|
|
92730
92799
|
// consistently with the chart's X-axis ticks on the same data.
|
|
@@ -96163,7 +96232,18 @@
|
|
|
96163
96232
|
copyChat: "Copy Chat",
|
|
96164
96233
|
startChat: "Start Chat",
|
|
96165
96234
|
startingVoiceSession: "Starting voice session...",
|
|
96166
|
-
allowMicAccess: "Allow microphone access"
|
|
96235
|
+
allowMicAccess: "Allow microphone access",
|
|
96236
|
+
// Onboarding-form validation messages. These are the English fallback for
|
|
96237
|
+
// every locale (translate() falls back to EN[key]); without them the form
|
|
96238
|
+
// rendered the raw key strings ("emailInvalid", etc.). `fieldRequiredSuffix`
|
|
96239
|
+
// is appended after a field label: `${label} ${fieldRequiredSuffix}`.
|
|
96240
|
+
emailRequired: "Please enter your email address",
|
|
96241
|
+
nameRequired: "Please enter your name",
|
|
96242
|
+
phoneRequired: "Please enter your phone number",
|
|
96243
|
+
fieldRequiredSuffix: "is required",
|
|
96244
|
+
emailInvalid: "Please enter a valid email address",
|
|
96245
|
+
nameInvalid: "Please enter a valid name",
|
|
96246
|
+
phoneInvalid: "Please enter a valid phone number"
|
|
96167
96247
|
};
|
|
96168
96248
|
const DICTIONARIES = {
|
|
96169
96249
|
en: EN,
|
|
@@ -99553,6 +99633,7 @@
|
|
|
99553
99633
|
apiBaseUrl,
|
|
99554
99634
|
apiKey,
|
|
99555
99635
|
organizationId,
|
|
99636
|
+
secretKey,
|
|
99556
99637
|
conversationId,
|
|
99557
99638
|
conciergeId,
|
|
99558
99639
|
onboardingQuestions = [],
|
|
@@ -100349,6 +100430,7 @@
|
|
|
100349
100430
|
/**
|
|
100350
100431
|
* @param {string|undefined|null} apiBaseUrl
|
|
100351
100432
|
* @param {string|undefined|null} orgId
|
|
100433
|
+
* @param {string|undefined|null} secretKey — Mode A/B: orgId resolved server-side
|
|
100352
100434
|
* @returns {{
|
|
100353
100435
|
* categories: Array<{id:string,label:string,count:number,tags:string[]}>,
|
|
100354
100436
|
* loading: boolean,
|
|
@@ -100356,14 +100438,17 @@
|
|
|
100356
100438
|
* hasFilters: boolean,
|
|
100357
100439
|
* }}
|
|
100358
100440
|
*/
|
|
100359
|
-
function useFilterCatalog(apiBaseUrl, orgId) {
|
|
100441
|
+
function useFilterCatalog(apiBaseUrl, orgId, secretKey) {
|
|
100442
|
+
// Mode A: secretKey present, orgId null → key-based endpoint
|
|
100443
|
+
// Mode C: orgId present (legacy) → orgId-in-path endpoint
|
|
100444
|
+
const hasAuth = !!(apiBaseUrl && (orgId || secretKey));
|
|
100360
100445
|
const [state, setState] = reactExports.useState({
|
|
100361
100446
|
categories: [],
|
|
100362
|
-
loading:
|
|
100447
|
+
loading: hasAuth,
|
|
100363
100448
|
error: null
|
|
100364
100449
|
});
|
|
100365
100450
|
reactExports.useEffect(() => {
|
|
100366
|
-
if (!apiBaseUrl || !orgId) {
|
|
100451
|
+
if (!apiBaseUrl || !orgId && !secretKey) {
|
|
100367
100452
|
setState({
|
|
100368
100453
|
categories: [],
|
|
100369
100454
|
loading: false,
|
|
@@ -100371,8 +100456,8 @@
|
|
|
100371
100456
|
});
|
|
100372
100457
|
return;
|
|
100373
100458
|
}
|
|
100374
|
-
const
|
|
100375
|
-
const cached = _PROCESS_CACHE.get(
|
|
100459
|
+
const cacheKey = secretKey ? "".concat(apiBaseUrl, "|__key__") : "".concat(apiBaseUrl, "|").concat(orgId);
|
|
100460
|
+
const cached = _PROCESS_CACHE.get(cacheKey);
|
|
100376
100461
|
if (cached) {
|
|
100377
100462
|
setState({
|
|
100378
100463
|
categories: cached,
|
|
@@ -100386,12 +100471,15 @@
|
|
|
100386
100471
|
loading: true,
|
|
100387
100472
|
error: null
|
|
100388
100473
|
}));
|
|
100389
|
-
const
|
|
100474
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100475
|
+
const url = secretKey ? "".concat(base, "/api/v1/widget/filters") : "".concat(base, "/api/v1/widget/filters/").concat(encodeURIComponent(orgId));
|
|
100476
|
+
const headers = {
|
|
100477
|
+
Accept: "application/json"
|
|
100478
|
+
};
|
|
100479
|
+
if (secretKey) headers["x-eshal-key"] = secretKey;
|
|
100390
100480
|
fetch(url, {
|
|
100391
100481
|
method: "GET",
|
|
100392
|
-
headers
|
|
100393
|
-
Accept: "application/json"
|
|
100394
|
-
}
|
|
100482
|
+
headers
|
|
100395
100483
|
}).then(async res => {
|
|
100396
100484
|
if (!res.ok) {
|
|
100397
100485
|
throw new Error("filter catalog HTTP ".concat(res.status));
|
|
@@ -100400,7 +100488,7 @@
|
|
|
100400
100488
|
return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100401
100489
|
}).then(categories => {
|
|
100402
100490
|
if (cancelled) return;
|
|
100403
|
-
_PROCESS_CACHE.set(
|
|
100491
|
+
_PROCESS_CACHE.set(cacheKey, categories);
|
|
100404
100492
|
setState({
|
|
100405
100493
|
categories,
|
|
100406
100494
|
loading: false,
|
|
@@ -100422,7 +100510,7 @@
|
|
|
100422
100510
|
return () => {
|
|
100423
100511
|
cancelled = true;
|
|
100424
100512
|
};
|
|
100425
|
-
}, [apiBaseUrl, orgId]);
|
|
100513
|
+
}, [apiBaseUrl, orgId, secretKey]);
|
|
100426
100514
|
const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
|
|
100427
100515
|
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
100428
100516
|
hasFilters
|
|
@@ -100504,17 +100592,25 @@
|
|
|
100504
100592
|
* @param {string} orgId - Organization ID
|
|
100505
100593
|
* @returns {Promise<object>} Agent configuration object
|
|
100506
100594
|
*/
|
|
100507
|
-
const fetchAgentConfig = async (apiBaseUrl, orgId) => {
|
|
100508
|
-
if (!apiBaseUrl
|
|
100509
|
-
throw new Error("apiBaseUrl
|
|
100595
|
+
const fetchAgentConfig = async (apiBaseUrl, orgId, secretKey) => {
|
|
100596
|
+
if (!apiBaseUrl) {
|
|
100597
|
+
throw new Error("apiBaseUrl is required");
|
|
100598
|
+
}
|
|
100599
|
+
if (!secretKey && !orgId) {
|
|
100600
|
+
throw new Error("secretKey or orgId is required");
|
|
100510
100601
|
}
|
|
100511
100602
|
try {
|
|
100512
|
-
const
|
|
100603
|
+
const base = apiBaseUrl.replace(/\/$/, '');
|
|
100604
|
+
// Mode A/B: no orgId in URL — backend resolves org from x-eshal-key or HMAC sig
|
|
100605
|
+
// Mode C legacy: orgId in URL path, no header needed
|
|
100606
|
+
const url = secretKey ? "".concat(base, "/api/v1/deploy-agent") : "".concat(base, "/api/v1/deploy-agent/").concat(orgId);
|
|
100607
|
+
const headers = {
|
|
100608
|
+
"Content-Type": "application/json"
|
|
100609
|
+
};
|
|
100610
|
+
if (secretKey) headers['x-eshal-key'] = secretKey;
|
|
100513
100611
|
const response = await fetch(url, {
|
|
100514
100612
|
method: "GET",
|
|
100515
|
-
headers
|
|
100516
|
-
"Content-Type": "application/json"
|
|
100517
|
-
},
|
|
100613
|
+
headers,
|
|
100518
100614
|
credentials: "include"
|
|
100519
100615
|
});
|
|
100520
100616
|
if (!response.ok) {
|
|
@@ -100531,8 +100627,11 @@
|
|
|
100531
100627
|
const ChatWidget = _ref => {
|
|
100532
100628
|
var _agentConfig$concierg;
|
|
100533
100629
|
let {
|
|
100534
|
-
//
|
|
100630
|
+
// Auth — at least one of secretKey (Mode A/B) or orgId (Mode C legacy) is needed.
|
|
100631
|
+
secretKey,
|
|
100632
|
+
// sk_live_... from ChatWidget.init({ secretKey, apiBaseUrl })
|
|
100535
100633
|
orgId,
|
|
100634
|
+
// legacy — kept for backward compatibility
|
|
100536
100635
|
apiBaseUrl,
|
|
100537
100636
|
// Optional override props (will override fetched config if provided)
|
|
100538
100637
|
darkMode,
|
|
@@ -100583,14 +100682,21 @@
|
|
|
100583
100682
|
// Fetch agent configuration on mount
|
|
100584
100683
|
reactExports.useEffect(() => {
|
|
100585
100684
|
const loadAgentConfig = async () => {
|
|
100586
|
-
if (!
|
|
100587
|
-
setConfigError("
|
|
100685
|
+
if (!apiBaseUrl) {
|
|
100686
|
+
setConfigError("apiBaseUrl is required");
|
|
100687
|
+
setIsLoadingConfig(false);
|
|
100688
|
+
return;
|
|
100689
|
+
}
|
|
100690
|
+
if (!secretKey && !orgId) {
|
|
100691
|
+
setConfigError("secretKey or orgId is required");
|
|
100588
100692
|
setIsLoadingConfig(false);
|
|
100589
100693
|
return;
|
|
100590
100694
|
}
|
|
100591
100695
|
try {
|
|
100592
100696
|
setIsLoadingConfig(true);
|
|
100593
|
-
|
|
100697
|
+
// Pass secretKey for Mode A/B — fetchAgentConfig will send it as a
|
|
100698
|
+
// header and omit the orgId path segment. Pass orgId for Mode C legacy.
|
|
100699
|
+
const config = await fetchAgentConfig(apiBaseUrl, orgId, secretKey);
|
|
100594
100700
|
setAgentConfig(config);
|
|
100595
100701
|
setConfigError(null);
|
|
100596
100702
|
} catch (error) {
|
|
@@ -100601,7 +100707,7 @@
|
|
|
100601
100707
|
}
|
|
100602
100708
|
};
|
|
100603
100709
|
loadAgentConfig();
|
|
100604
|
-
}, [orgId, apiBaseUrl]);
|
|
100710
|
+
}, [orgId, secretKey, apiBaseUrl]);
|
|
100605
100711
|
|
|
100606
100712
|
// Map API response to widget props (use props as overrides if provided)
|
|
100607
100713
|
const widgetConfig = reactExports.useMemo(() => {
|
|
@@ -100779,6 +100885,7 @@
|
|
|
100779
100885
|
// knowledge-api LoadBalancer.
|
|
100780
100886
|
apiKey,
|
|
100781
100887
|
organizationId: widgetConfig.organizationId,
|
|
100888
|
+
secretKey,
|
|
100782
100889
|
autoOpen,
|
|
100783
100890
|
openDelay,
|
|
100784
100891
|
darkMode: widgetConfig.darkMode,
|
|
@@ -100805,14 +100912,15 @@
|
|
|
100805
100912
|
// navigation (but not a page refresh — feedback is not written to
|
|
100806
100913
|
// localStorage so it resets on reload as intended).
|
|
100807
100914
|
updateMessageFeedback(messageContent, feedback);
|
|
100808
|
-
if (!orgId || !conversationId || !apiBaseUrl) return;
|
|
100915
|
+
if (!orgId && !secretKey || !conversationId || !apiBaseUrl) return;
|
|
100809
100916
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100917
|
+
const feedbackHeaders = {
|
|
100918
|
+
"Content-Type": "application/json"
|
|
100919
|
+
};
|
|
100920
|
+
if (secretKey) feedbackHeaders['x-eshal-key'] = secretKey;else if (orgId) feedbackHeaders['x-eshal-org'] = orgId;
|
|
100810
100921
|
fetch("".concat(base, "/api/v1/support/feedback"), {
|
|
100811
100922
|
method: "POST",
|
|
100812
|
-
headers:
|
|
100813
|
-
"Content-Type": "application/json",
|
|
100814
|
-
"x-eshal-org": orgId
|
|
100815
|
-
},
|
|
100923
|
+
headers: feedbackHeaders,
|
|
100816
100924
|
credentials: "include",
|
|
100817
100925
|
body: JSON.stringify({
|
|
100818
100926
|
messageContent,
|
|
@@ -100838,7 +100946,7 @@
|
|
|
100838
100946
|
const {
|
|
100839
100947
|
categories: filterCategories,
|
|
100840
100948
|
hasFilters: showFilterStep
|
|
100841
|
-
} = useFilterCatalog(apiBaseUrl, orgId);
|
|
100949
|
+
} = useFilterCatalog(apiBaseUrl, orgId, secretKey);
|
|
100842
100950
|
|
|
100843
100951
|
// Copy chat handler — copy all messages to clipboard
|
|
100844
100952
|
const handleCopyChat = reactExports.useCallback(() => {
|
|
@@ -100944,6 +101052,7 @@
|
|
|
100944
101052
|
apiBaseUrl: apiBaseUrl,
|
|
100945
101053
|
apiKey: apiKey,
|
|
100946
101054
|
organizationId: widgetConfig.organizationId,
|
|
101055
|
+
secretKey: secretKey,
|
|
100947
101056
|
conversationId: conversationId,
|
|
100948
101057
|
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,
|
|
100949
101058
|
onboardingQuestions: widgetConfig.onboardingQuestions,
|
|
@@ -101091,11 +101200,14 @@
|
|
|
101091
101200
|
}
|
|
101092
101201
|
|
|
101093
101202
|
// Render component
|
|
101094
|
-
//
|
|
101095
|
-
//
|
|
101096
|
-
|
|
101097
|
-
|
|
101098
|
-
|
|
101203
|
+
// Mode A/B: secretKey present — orgId derived server-side from the key
|
|
101204
|
+
// Mode C legacy: orgId in config — backward compatible
|
|
101205
|
+
// Both: apiBaseUrl required
|
|
101206
|
+
const hasNewAuth = (config.secretKey || config.orgId) && config.apiBaseUrl;
|
|
101207
|
+
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({
|
|
101208
|
+
// Auth props — secretKey replaces orgId in Mode A/B
|
|
101209
|
+
secretKey: config.secretKey || null,
|
|
101210
|
+
orgId: config.orgId || null,
|
|
101099
101211
|
apiBaseUrl: config.apiBaseUrl
|
|
101100
101212
|
}, options.wsBaseUrl !== undefined && {
|
|
101101
101213
|
wsBaseUrl: config.wsBaseUrl
|