@cusuai/web-sdk 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -19,8 +19,8 @@ npm install @cusuai/web-sdk
19
19
  - Optional **Realtime voice** calls (WebRTC; server-minted session)
20
20
  - Dictation via `/v1/transcribe` (Bearer public key)
21
21
  - `identify` for logged-in customers (optional HMAC signature from your backend)
22
- - Locales: English (`en`), Bulgarian (`bg`), Czech (`cs`), Slovak (`sk`), Spanish (`es`), German (`de`), Estonian (`et`), French (`fr`), Polish (`pl`), Hungarian (`hu`), Italian (`it`), Lithuanian (`lt`), Latvian (`lv`), Dutch (`nl`), Norwegian Bokmål (`no`), Portuguese (`pt`), Danish (`da`), Slovenian (`sl`), Croatian (`hr`), Romanian (`ro`), Swedish (`sv`), and Finnish (`fi`); chrome follows the group language from boot
23
- - Published builds: **ES module** (`dist/index.js`) + **IIFE** (`dist/cusu.iife.js`, global `Cusu`)
22
+ - Locales: English (`en`), Bulgarian (`bg`), Czech (`cs`), Slovak (`sk`), Spanish (`es`), German (`de`), Estonian (`et`), French (`fr`), Polish (`pl`), Hungarian (`hu`), Italian (`it`), Lithuanian (`lt`), Latvian (`lv`), Dutch (`nl`), Norwegian Bokmål (`no`), Portuguese (`pt`), Danish (`da`), Slovenian (`sl`), Croatian (`hr`), Romanian (`ro`), Swedish (`sv`), and Finnish (`fi`); chrome follows group language from boot, or page/`config.locale` when reply locale is `auto`
23
+ - Published builds: **ES module** (`dist/index.js`) + **IIFE** script tag on the Zerops CDN (`widget/<version>/cusu.js`, global `Cusu`)
24
24
 
25
25
  ---
26
26
 
@@ -50,7 +50,7 @@ Cusu.onError((error) => {
50
50
  Cusu.initialize({
51
51
  group: 'grp_…',
52
52
  apiKey: 'pk_…',
53
- // locale: 'en', // optional fallback if boot has no renderable language
53
+ // locale: 'en', // optional; used when reply locale is auto (or as fallback if boot has no language)
54
54
  // showLauncher: true // default; set false for a headless launcher
55
55
  });
56
56
 
@@ -84,12 +84,12 @@ import Cusu from '@cusuai/web-sdk';
84
84
 
85
85
  Types ship with the package (`dist/index.d.ts`).
86
86
 
87
- ### Script tag (IIFE)
87
+ ### Script tag (Shopify, Shoptet, plain HTML)
88
88
 
89
- After install (or from your CDN of the published `dist/`):
89
+ No package manager. Paste this before `</body>` in the theme (`theme.liquid` on Shopify). Pin the version — `latest` can stay cached for up to 30 days.
90
90
 
91
91
  ```html
92
- <script src="https://cdn.example.com/cusu.iife.js"></script>
92
+ <script src="https://storage.cdn.zerops.app/4gfpg-widgetcdn/widget/0.2.0/cusu.js"></script>
93
93
  <script>
94
94
  Cusu.initialize({
95
95
  group: 'grp_…',
@@ -98,7 +98,7 @@ After install (or from your CDN of the published `dist/`):
98
98
  </script>
99
99
  ```
100
100
 
101
- The IIFE build exposes a global `Cusu` with the same API as the default export.
101
+ The script exposes a global `Cusu` with the same API as the default export. The shop origin must be on the public key's allowed origins. Layout, cache, and how a release publishes the file: [docs/widget-cdn.md](./docs/widget-cdn.md).
102
102
 
103
103
  ---
104
104
 
@@ -111,7 +111,7 @@ The IIFE build exposes a global `Cusu` with the same API as the default export.
111
111
  | `group` | `string` | yes | Stable group id (`grp_…`) from your Cusu install |
112
112
  | `apiKey` | `string` | yes | Public API key (`pk_…`) |
113
113
  | `apiUrl` | `string` | no | API base URL. Defaults to `https://api.cusuai.com` |
114
- | `locale` | `'en' \| 'bg' \| 'cs' \| 'sk' \| 'es' \| 'de' \| 'et' \| 'fr' \| 'pl' \| 'hu' \| 'it' \| 'lt' \| 'lv' \| 'nl' \| 'no' \| 'pt' \| 'da' \| 'sl' \| 'hr' \| 'ro' \| 'sv' \| 'fi'` | no | Fallback UI locale when boot language is missing/unsupported |
114
+ | `locale` | `'en' \| 'bg' \| 'cs' \| 'sk' \| 'es' \| 'de' \| 'et' \| 'fr' \| 'pl' \| 'hu' \| 'it' \| 'lt' \| 'lv' \| 'nl' \| 'no' \| 'pt' \| 'da' \| 'sl' \| 'hr' \| 'ro' \| 'sv' \| 'fi'` | no | Preferred UI locale when boot `reply_locale` is `auto`; otherwise fallback if boot language is missing/unsupported |
115
115
  | `showLauncher` | `boolean` | no | Floating FAB; default `true` |
116
116
 
117
117
  Calling `initialize` again tears down the previous instance and boots a new one.
@@ -181,73 +181,50 @@ Cusu.identify('user_123', {
181
181
 
182
182
  ### Signed identify (recommended in production)
183
183
 
184
- When the group has an identify secret (`isk_…`), unsigned `identify` is **rejected**. Sign on your **backend** only.
184
+ When the group has an identify secret (`isk_…`), unsigned `identify` is **rejected**. Sign on your **backend** only — use [`@cusuai/node`](https://github.com/cusuai/cusu-node-sdk) `identifySign` (do not put `isk_…` in the browser).
185
185
 
186
- 1. Read (or set) the first-party cookie `cusu_vid` — that value is the `visitorId`.
187
- 2. Build the canonical string (empty optional fields are empty segments):
188
-
189
- ```text
190
- v1\n{visitorId}\n{externalId}\n{signedAt}\n{name}\n{email}\n{phone}\n{gender}
186
+ ```bash
187
+ npm install @cusuai/node
191
188
  ```
192
189
 
193
- 3. `signature = hex(HMAC-SHA256(secret, canonical))` with `signedAt = Date.now()` (skew window ±5 minutes).
194
- 4. Return traits + `signedAt` + `signature` to the browser; pass them into `Cusu.identify`.
195
-
196
- **Node.js example** (Express / any backend):
190
+ ```ts
191
+ import { identifySign } from '@cusuai/node';
197
192
 
198
- ```js
199
- import { createHmac } from 'node:crypto';
200
-
201
- function signIdentify(secret, { visitorId, externalId, signedAt, name, email, phone, gender }) {
202
- const canonical = [
203
- 'v1',
204
- visitorId,
205
- externalId,
206
- String(signedAt),
207
- name ?? '',
208
- email ?? '',
209
- phone ?? '',
210
- gender ?? ''
211
- ].join('\n');
212
- return createHmac('sha256', secret).update(canonical, 'utf8').digest('hex');
213
- }
214
-
215
- // POST /api/cusu-identify
193
+ // POST /api/cusu-identify — body: { visitorId } from the browser (cusu_vid cookie).
194
+ // Take externalId + traits from your authenticated session / DB.
216
195
  app.post('/api/cusu-identify', (req, res) => {
217
- const externalId = String(req.body.externalId ?? '').trim();
218
- const traits = req.body.traits ?? {};
219
- const visitorId = req.cookies.cusu_vid ?? crypto.randomUUID();
220
- res.cookie('cusu_vid', visitorId, { path: '/', maxAge: 400 * 24 * 60 * 60 * 1000, sameSite: 'lax' });
221
-
222
- const signedAt = Date.now();
223
- const signature = signIdentify(process.env.CUSU_IDENTIFY_SECRET, {
224
- visitorId,
225
- externalId,
226
- signedAt,
227
- name: traits.name,
228
- email: traits.email,
229
- phone: traits.phone,
230
- gender: traits.gender
231
- });
232
-
233
- res.json({ ...traits, signedAt, signature });
196
+ const visitorId = String(req.body.visitorId ?? '').trim();
197
+ res.json(
198
+ identifySign({
199
+ isk: process.env.CUSU_IDENTIFY_SECRET!,
200
+ visitorId,
201
+ externalId: req.user.id,
202
+ traits: {
203
+ name: req.user.name,
204
+ email: req.user.email,
205
+ gender: req.user.gender
206
+ }
207
+ })
208
+ );
234
209
  });
235
210
  ```
236
211
 
237
212
  Browser:
238
213
 
239
214
  ```js
215
+ const visitorId = document.cookie
216
+ .split('; ')
217
+ .find((row) => row.startsWith('cusu_vid='))
218
+ ?.split('=')[1];
219
+
240
220
  const identity = await fetch('/api/cusu-identify', {
241
221
  method: 'POST',
242
222
  headers: { 'content-type': 'application/json' },
243
223
  credentials: 'same-origin',
244
- body: JSON.stringify({
245
- externalId: 'user_123',
246
- traits: { name: 'Jane Doe', email: 'jane@shop.test', gender: 'female' }
247
- })
224
+ body: JSON.stringify({ visitorId })
248
225
  }).then((r) => r.json());
249
226
 
250
- Cusu.identify('user_123', identity);
227
+ Cusu.identify(identity.externalId, identity);
251
228
  ```
252
229
 
253
230
  On **logout**, call `reset()` before the next `identify`. `destroy()` + `initialize()` is not enough: the same `cusu_vid` cookie and local conversation list would keep the previous customer's threads. After `identify`, the SDK replaces local history with that customer's threads from the server.
@@ -256,7 +233,7 @@ On **logout**, call `reset()` before the next `identify`. `destroy()` + `initial
256
233
  Cusu.reset();
257
234
  ```
258
235
 
259
- Full protocol notes: [docs/protocol.md](./docs/protocol.md). Integrator security checklist: [SECURITY.md](./SECURITY.md).
236
+ Package docs: [cusuai/cusu-node-sdk](https://github.com/cusuai/cusu-node-sdk). Full protocol notes: [docs/protocol.md](./docs/protocol.md). Integrator security checklist: [SECURITY.md](./SECURITY.md).
260
237
 
261
238
  ---
262
239