@usecrow/client 0.1.43 → 0.1.45
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/index.cjs +1 -3
- package/dist/index.d.ts +57 -9
- package/dist/index.js +50 -790
- package/dist/index.native.cjs +3 -0
- package/dist/index.native.d.ts +517 -0
- package/dist/index.native.js +788 -0
- package/package.json +3 -1
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
class I {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.state = {
|
|
4
|
+
token: null,
|
|
5
|
+
metadata: {},
|
|
6
|
+
isVerified: !1
|
|
7
|
+
}, this.listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Identify the current user with a JWT token
|
|
11
|
+
*/
|
|
12
|
+
identify(e) {
|
|
13
|
+
const { token: t, ...s } = e;
|
|
14
|
+
this.state = {
|
|
15
|
+
token: t,
|
|
16
|
+
metadata: s,
|
|
17
|
+
isVerified: !1
|
|
18
|
+
// Will be set when server confirms
|
|
19
|
+
}, this.notify(), console.log("[Crow] User identified");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Update verification status (called when server confirms)
|
|
23
|
+
*/
|
|
24
|
+
setVerified(e) {
|
|
25
|
+
this.state = { ...this.state, isVerified: e }, this.notify();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Reset user identity (call on logout)
|
|
29
|
+
*/
|
|
30
|
+
reset() {
|
|
31
|
+
this.state = {
|
|
32
|
+
token: null,
|
|
33
|
+
metadata: {},
|
|
34
|
+
isVerified: !1
|
|
35
|
+
}, this.notify(), console.log("[Crow] User reset");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get current identity token
|
|
39
|
+
*/
|
|
40
|
+
getToken() {
|
|
41
|
+
return this.state.token;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get current identity state
|
|
45
|
+
*/
|
|
46
|
+
getState() {
|
|
47
|
+
return { ...this.state };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if user is identified
|
|
51
|
+
*/
|
|
52
|
+
isIdentified() {
|
|
53
|
+
return this.state.token !== null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if user is verified
|
|
57
|
+
*/
|
|
58
|
+
isVerified() {
|
|
59
|
+
return this.state.isVerified;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Subscribe to identity changes
|
|
63
|
+
*/
|
|
64
|
+
subscribe(e) {
|
|
65
|
+
return this.listeners.add(e), () => this.listeners.delete(e);
|
|
66
|
+
}
|
|
67
|
+
notify() {
|
|
68
|
+
const e = this.getState();
|
|
69
|
+
for (const t of this.listeners)
|
|
70
|
+
t(e);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
class C {
|
|
74
|
+
constructor() {
|
|
75
|
+
this.handlers = {};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Register client-side tool handlers
|
|
79
|
+
*/
|
|
80
|
+
register(e) {
|
|
81
|
+
for (const [t, s] of Object.entries(e))
|
|
82
|
+
typeof s == "function" ? (this.handlers[t] = s, console.log(`[Crow] Registered client tool: ${t}`)) : console.warn(`[Crow] Skipping ${t}: handler is not a function`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Unregister a tool handler
|
|
86
|
+
*/
|
|
87
|
+
unregister(e) {
|
|
88
|
+
delete this.handlers[e], console.log(`[Crow] Unregistered client tool: ${e}`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if a tool is registered
|
|
92
|
+
*/
|
|
93
|
+
has(e) {
|
|
94
|
+
return e in this.handlers;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get all registered tool names
|
|
98
|
+
*/
|
|
99
|
+
getRegisteredTools() {
|
|
100
|
+
return Object.keys(this.handlers);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Execute a client-side tool
|
|
104
|
+
*/
|
|
105
|
+
async execute(e, t) {
|
|
106
|
+
const s = this.handlers[e];
|
|
107
|
+
if (!s)
|
|
108
|
+
return console.warn(`[Crow] No handler registered for tool: ${e}`), {
|
|
109
|
+
status: "error",
|
|
110
|
+
error: `No handler registered for tool: ${e}`
|
|
111
|
+
};
|
|
112
|
+
try {
|
|
113
|
+
console.log(`[Crow] Executing client tool: ${e}`, t);
|
|
114
|
+
const r = await s(t);
|
|
115
|
+
return console.log(`[Crow] Tool ${e} completed:`, r), r;
|
|
116
|
+
} catch (r) {
|
|
117
|
+
const i = r instanceof Error ? r.message : String(r);
|
|
118
|
+
return console.error(`[Crow] Tool ${e} failed:`, r), {
|
|
119
|
+
status: "error",
|
|
120
|
+
error: i
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const b = "crow_conv_";
|
|
126
|
+
class S {
|
|
127
|
+
constructor(e, t, s) {
|
|
128
|
+
this.currentId = null, this.productId = e, this.apiUrl = t, this.storage = s, this.currentId = this.loadFromStorage();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get storage key for this product
|
|
132
|
+
*/
|
|
133
|
+
getStorageKey() {
|
|
134
|
+
return `${b}${this.productId}`;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Load conversation ID from storage
|
|
138
|
+
*/
|
|
139
|
+
loadFromStorage() {
|
|
140
|
+
try {
|
|
141
|
+
return this.storage.getItem(this.getStorageKey());
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Save conversation ID to storage
|
|
148
|
+
*/
|
|
149
|
+
saveToStorage(e) {
|
|
150
|
+
try {
|
|
151
|
+
this.storage.setItem(this.getStorageKey(), e);
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Clear conversation ID from storage
|
|
157
|
+
*/
|
|
158
|
+
clearStorage() {
|
|
159
|
+
try {
|
|
160
|
+
this.storage.removeItem(this.getStorageKey());
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get current conversation ID
|
|
166
|
+
*/
|
|
167
|
+
getCurrentId() {
|
|
168
|
+
return this.currentId;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Set current conversation ID
|
|
172
|
+
*/
|
|
173
|
+
setCurrentId(e) {
|
|
174
|
+
this.currentId = e, e ? this.saveToStorage(e) : this.clearStorage();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if there's a restored conversation
|
|
178
|
+
*/
|
|
179
|
+
hasRestoredConversation() {
|
|
180
|
+
return this.currentId !== null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Clear current conversation (start new)
|
|
184
|
+
*/
|
|
185
|
+
clear() {
|
|
186
|
+
this.currentId = null, this.clearStorage();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Fetch list of conversations for verified user
|
|
190
|
+
*/
|
|
191
|
+
async getConversations(e) {
|
|
192
|
+
try {
|
|
193
|
+
const t = await fetch(
|
|
194
|
+
`${this.apiUrl}/api/chat/conversations?product_id=${this.productId}&identity_token=${encodeURIComponent(e)}`
|
|
195
|
+
);
|
|
196
|
+
if (!t.ok)
|
|
197
|
+
throw new Error(`HTTP error: ${t.status}`);
|
|
198
|
+
return (await t.json()).conversations || [];
|
|
199
|
+
} catch (t) {
|
|
200
|
+
return console.error("[Crow] Failed to load conversations:", t), [];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Load conversation history for verified user
|
|
205
|
+
*/
|
|
206
|
+
async loadHistory(e, t) {
|
|
207
|
+
try {
|
|
208
|
+
const s = await fetch(
|
|
209
|
+
`${this.apiUrl}/api/chat/conversations/${e}/history?product_id=${this.productId}&identity_token=${encodeURIComponent(t)}`
|
|
210
|
+
);
|
|
211
|
+
if (!s.ok)
|
|
212
|
+
throw new Error(`HTTP error: ${s.status}`);
|
|
213
|
+
const r = await s.json();
|
|
214
|
+
return this.parseHistoryMessages(r.messages || []);
|
|
215
|
+
} catch (s) {
|
|
216
|
+
return console.error("[Crow] Failed to load conversation history:", s), [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Load conversation history for anonymous user
|
|
221
|
+
*/
|
|
222
|
+
async loadAnonymousHistory(e) {
|
|
223
|
+
try {
|
|
224
|
+
const t = await fetch(
|
|
225
|
+
`${this.apiUrl}/api/chat/conversations/${e}/history/anonymous?product_id=${this.productId}`
|
|
226
|
+
);
|
|
227
|
+
if (!t.ok)
|
|
228
|
+
throw new Error(`HTTP error: ${t.status}`);
|
|
229
|
+
const s = await t.json();
|
|
230
|
+
return this.parseHistoryMessages(s.messages || []);
|
|
231
|
+
} catch (t) {
|
|
232
|
+
return console.error("[Crow] Failed to load anonymous conversation history:", t), [];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Parse history messages from API format
|
|
237
|
+
*/
|
|
238
|
+
parseHistoryMessages(e) {
|
|
239
|
+
return e.filter((t) => t.role !== "tool" && !t.content.startsWith("[Client Tool Result:")).map((t, s) => ({
|
|
240
|
+
id: `history-${s}`,
|
|
241
|
+
content: this.parseContent(t.content),
|
|
242
|
+
role: t.role === "assistant" ? "assistant" : "user",
|
|
243
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Parse structured content (with thinking/text blocks) and extract just text
|
|
248
|
+
*/
|
|
249
|
+
parseContent(e) {
|
|
250
|
+
try {
|
|
251
|
+
const t = JSON.parse(e);
|
|
252
|
+
if (Array.isArray(t)) {
|
|
253
|
+
const s = t.find(
|
|
254
|
+
(r) => r.type === "text"
|
|
255
|
+
);
|
|
256
|
+
return (s == null ? void 0 : s.text) || e;
|
|
257
|
+
}
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
if (e.includes("'type': 'text'")) {
|
|
261
|
+
const t = e.match(
|
|
262
|
+
/\{'text':\s*'((?:[^'\\]|\\.)*)'\s*,\s*'type':\s*'text'/
|
|
263
|
+
);
|
|
264
|
+
if (t)
|
|
265
|
+
return t[1].replace(/\\n/g, `
|
|
266
|
+
`).replace(/\\'/g, "'");
|
|
267
|
+
}
|
|
268
|
+
return e;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function M(o) {
|
|
272
|
+
if (o === "[DONE]")
|
|
273
|
+
return { type: "done" };
|
|
274
|
+
try {
|
|
275
|
+
const e = JSON.parse(o);
|
|
276
|
+
switch (e.type) {
|
|
277
|
+
case "verification_status":
|
|
278
|
+
return {
|
|
279
|
+
type: "verification_status",
|
|
280
|
+
isVerified: e.is_verified === !0
|
|
281
|
+
};
|
|
282
|
+
case "conversation_id":
|
|
283
|
+
return {
|
|
284
|
+
type: "conversation_id",
|
|
285
|
+
conversationId: e.conversation_id
|
|
286
|
+
};
|
|
287
|
+
case "thinking":
|
|
288
|
+
return e.status === "complete" ? { type: "thinking_complete" } : null;
|
|
289
|
+
case "thinking_token":
|
|
290
|
+
return {
|
|
291
|
+
type: "thinking",
|
|
292
|
+
content: e.content || ""
|
|
293
|
+
};
|
|
294
|
+
case "content":
|
|
295
|
+
return {
|
|
296
|
+
type: "content",
|
|
297
|
+
text: e.content || "",
|
|
298
|
+
accumulated: ""
|
|
299
|
+
// Will be set by caller
|
|
300
|
+
};
|
|
301
|
+
case "citations":
|
|
302
|
+
return {
|
|
303
|
+
type: "citations",
|
|
304
|
+
citations: e.citations
|
|
305
|
+
};
|
|
306
|
+
case "error":
|
|
307
|
+
return {
|
|
308
|
+
type: "error",
|
|
309
|
+
message: e.message || "Unknown error"
|
|
310
|
+
};
|
|
311
|
+
case "tool_call_start":
|
|
312
|
+
return {
|
|
313
|
+
type: "tool_call_start",
|
|
314
|
+
toolName: e.tool_name,
|
|
315
|
+
displayName: e.display_name || void 0,
|
|
316
|
+
arguments: e.arguments || {}
|
|
317
|
+
};
|
|
318
|
+
case "tool_call_complete":
|
|
319
|
+
return {
|
|
320
|
+
type: "tool_call_complete",
|
|
321
|
+
toolName: e.tool_name,
|
|
322
|
+
displayName: e.display_name || void 0,
|
|
323
|
+
success: e.success
|
|
324
|
+
};
|
|
325
|
+
case "client_tool_call":
|
|
326
|
+
return {
|
|
327
|
+
type: "client_tool_call",
|
|
328
|
+
toolName: e.tool_name,
|
|
329
|
+
displayName: e.display_name || void 0,
|
|
330
|
+
arguments: e.arguments || {}
|
|
331
|
+
};
|
|
332
|
+
case "tool_consent_required":
|
|
333
|
+
return {
|
|
334
|
+
type: "tool_consent_required",
|
|
335
|
+
toolName: e.tool_name,
|
|
336
|
+
displayName: e.display_name || void 0,
|
|
337
|
+
arguments: e.arguments || {}
|
|
338
|
+
};
|
|
339
|
+
case "workflow_started":
|
|
340
|
+
return {
|
|
341
|
+
type: "workflow_started",
|
|
342
|
+
name: e.name,
|
|
343
|
+
todos: e.todos
|
|
344
|
+
};
|
|
345
|
+
case "todo_updated":
|
|
346
|
+
return {
|
|
347
|
+
type: "workflow_todo_updated",
|
|
348
|
+
todoId: e.id,
|
|
349
|
+
status: e.status
|
|
350
|
+
};
|
|
351
|
+
case "workflow_ended":
|
|
352
|
+
return { type: "workflow_ended" };
|
|
353
|
+
case "workflow_complete_prompt":
|
|
354
|
+
return { type: "workflow_complete_prompt" };
|
|
355
|
+
default:
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
} catch {
|
|
359
|
+
return console.error("[Crow] Failed to parse SSE data:", o), null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function* T(o) {
|
|
363
|
+
const e = o.split(`
|
|
364
|
+
`);
|
|
365
|
+
for (const t of e)
|
|
366
|
+
t.startsWith("data: ") && (yield t.slice(6).trim());
|
|
367
|
+
}
|
|
368
|
+
async function* $(o, e) {
|
|
369
|
+
var i;
|
|
370
|
+
const t = (i = o.body) == null ? void 0 : i.getReader();
|
|
371
|
+
if (!t)
|
|
372
|
+
throw new Error("Response body is not readable");
|
|
373
|
+
const s = new TextDecoder();
|
|
374
|
+
let r = "";
|
|
375
|
+
try {
|
|
376
|
+
for (; ; ) {
|
|
377
|
+
if (e != null && e.aborted) {
|
|
378
|
+
t.cancel();
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const { done: l, value: d } = await t.read();
|
|
382
|
+
if (l) break;
|
|
383
|
+
const h = s.decode(d);
|
|
384
|
+
for (const u of T(h)) {
|
|
385
|
+
const c = M(u);
|
|
386
|
+
if (c && (c.type === "content" ? (r += c.text, yield { ...c, accumulated: r }) : yield c, c.type === "done"))
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
} finally {
|
|
391
|
+
t.releaseLock();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const E = typeof window < "u" && typeof document < "u";
|
|
395
|
+
async function L() {
|
|
396
|
+
var o;
|
|
397
|
+
try {
|
|
398
|
+
const e = document.title, t = window.location.href, s = window.location.pathname, r = (((o = document.body) == null ? void 0 : o.innerText) || "").slice(0, 2e3).trim();
|
|
399
|
+
return {
|
|
400
|
+
status: "success",
|
|
401
|
+
data: {
|
|
402
|
+
title: e,
|
|
403
|
+
url: t,
|
|
404
|
+
pathname: s,
|
|
405
|
+
visibleText: r
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
} catch (e) {
|
|
409
|
+
return {
|
|
410
|
+
status: "error",
|
|
411
|
+
error: e instanceof Error ? e.message : "Failed to read screen"
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const _ = E ? { whatsOnScreen: L } : {}, w = Object.keys(_);
|
|
416
|
+
function x() {
|
|
417
|
+
try {
|
|
418
|
+
const o = "__crow_test__";
|
|
419
|
+
return localStorage.setItem(o, "1"), localStorage.removeItem(o), {
|
|
420
|
+
getItem: (e) => localStorage.getItem(e),
|
|
421
|
+
setItem: (e, t) => localStorage.setItem(e, t),
|
|
422
|
+
removeItem: (e) => localStorage.removeItem(e)
|
|
423
|
+
};
|
|
424
|
+
} catch {
|
|
425
|
+
return k();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function k() {
|
|
429
|
+
const o = /* @__PURE__ */ new Map();
|
|
430
|
+
return {
|
|
431
|
+
getItem: (e) => o.get(e) ?? null,
|
|
432
|
+
setItem: (e, t) => o.set(e, t),
|
|
433
|
+
removeItem: (e) => o.delete(e)
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
const U = "https://api.usecrow.org", R = "claude-sonnet-4-20250514", v = typeof window < "u" && typeof document < "u";
|
|
437
|
+
function N(o) {
|
|
438
|
+
return o.storage ? o.storage : v ? x() : k();
|
|
439
|
+
}
|
|
440
|
+
function O(o) {
|
|
441
|
+
return o.streamReader ? o.streamReader : $;
|
|
442
|
+
}
|
|
443
|
+
class A {
|
|
444
|
+
constructor(e) {
|
|
445
|
+
this.context = {}, this.abortController = null, this.callbacks = {}, this._messages = [], this.messageListeners = /* @__PURE__ */ new Set(), this._isLoading = !1, this.loadingListeners = /* @__PURE__ */ new Set(), this.config = {
|
|
446
|
+
productId: e.productId,
|
|
447
|
+
apiUrl: e.apiUrl || U,
|
|
448
|
+
model: e.model || R,
|
|
449
|
+
subdomain: e.subdomain
|
|
450
|
+
};
|
|
451
|
+
const t = N(e);
|
|
452
|
+
this.readStream = O(e), this.identity = new I(), this.tools = new C(), this.conversations = new S(
|
|
453
|
+
this.config.productId,
|
|
454
|
+
this.config.apiUrl,
|
|
455
|
+
t
|
|
456
|
+
), (e.enableBrowserTools ?? (v && w.length > 0)) && (this.tools.register(_), console.log("[Crow] Default tools registered:", w.join(", "))), this.identity.subscribe((r) => {
|
|
457
|
+
var i, l;
|
|
458
|
+
(l = (i = this.callbacks).onVerificationStatus) == null || l.call(i, r.isVerified);
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
// ============================================================================
|
|
462
|
+
// Configuration
|
|
463
|
+
// ============================================================================
|
|
464
|
+
/**
|
|
465
|
+
* Get current product ID
|
|
466
|
+
*/
|
|
467
|
+
get productId() {
|
|
468
|
+
return this.config.productId;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Get current API URL
|
|
472
|
+
*/
|
|
473
|
+
get apiUrl() {
|
|
474
|
+
return this.config.apiUrl;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Get/set current model
|
|
478
|
+
*/
|
|
479
|
+
get model() {
|
|
480
|
+
return this.config.model;
|
|
481
|
+
}
|
|
482
|
+
set model(e) {
|
|
483
|
+
this.config.model = e;
|
|
484
|
+
}
|
|
485
|
+
// ============================================================================
|
|
486
|
+
// Event Callbacks
|
|
487
|
+
// ============================================================================
|
|
488
|
+
/**
|
|
489
|
+
* Register event callbacks
|
|
490
|
+
*/
|
|
491
|
+
on(e) {
|
|
492
|
+
this.callbacks = { ...this.callbacks, ...e };
|
|
493
|
+
}
|
|
494
|
+
// ============================================================================
|
|
495
|
+
// Identity
|
|
496
|
+
// ============================================================================
|
|
497
|
+
/**
|
|
498
|
+
* Identify the current user with a JWT token
|
|
499
|
+
*/
|
|
500
|
+
identify(e) {
|
|
501
|
+
this.identity.identify(e);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Reset user identity (call on logout)
|
|
505
|
+
*/
|
|
506
|
+
resetUser() {
|
|
507
|
+
this.identity.reset(), this.clearMessages();
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Check if user is identified
|
|
511
|
+
*/
|
|
512
|
+
isIdentified() {
|
|
513
|
+
return this.identity.isIdentified();
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Check if user is verified by server
|
|
517
|
+
*/
|
|
518
|
+
isVerified() {
|
|
519
|
+
return this.identity.isVerified();
|
|
520
|
+
}
|
|
521
|
+
// ============================================================================
|
|
522
|
+
// Tools
|
|
523
|
+
// ============================================================================
|
|
524
|
+
/**
|
|
525
|
+
* Register client-side tool handlers
|
|
526
|
+
*/
|
|
527
|
+
registerTools(e) {
|
|
528
|
+
this.tools.register(e);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Unregister a tool handler
|
|
532
|
+
*/
|
|
533
|
+
unregisterTool(e) {
|
|
534
|
+
this.tools.unregister(e);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get list of registered tool names
|
|
538
|
+
*/
|
|
539
|
+
getRegisteredTools() {
|
|
540
|
+
return this.tools.getRegisteredTools();
|
|
541
|
+
}
|
|
542
|
+
// ============================================================================
|
|
543
|
+
// Context
|
|
544
|
+
// ============================================================================
|
|
545
|
+
/**
|
|
546
|
+
* Set context data to be sent with messages
|
|
547
|
+
*/
|
|
548
|
+
setContext(e) {
|
|
549
|
+
this.context = { ...this.context, ...e };
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Clear context data
|
|
553
|
+
*/
|
|
554
|
+
clearContext() {
|
|
555
|
+
this.context = {};
|
|
556
|
+
}
|
|
557
|
+
// ============================================================================
|
|
558
|
+
// Messages
|
|
559
|
+
// ============================================================================
|
|
560
|
+
/**
|
|
561
|
+
* Get current messages
|
|
562
|
+
*/
|
|
563
|
+
get messages() {
|
|
564
|
+
return [...this._messages];
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Check if currently loading/streaming
|
|
568
|
+
*/
|
|
569
|
+
get isLoading() {
|
|
570
|
+
return this._isLoading;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Subscribe to message changes
|
|
574
|
+
*/
|
|
575
|
+
onMessages(e) {
|
|
576
|
+
return this.messageListeners.add(e), () => this.messageListeners.delete(e);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Subscribe to loading state changes
|
|
580
|
+
*/
|
|
581
|
+
onLoading(e) {
|
|
582
|
+
return this.loadingListeners.add(e), () => this.loadingListeners.delete(e);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Clear all messages and start new conversation
|
|
586
|
+
*/
|
|
587
|
+
clearMessages() {
|
|
588
|
+
this._messages = [], this.conversations.clear(), this.notifyMessages();
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Load messages from history
|
|
592
|
+
*/
|
|
593
|
+
loadMessages(e) {
|
|
594
|
+
this._messages = e, this.notifyMessages();
|
|
595
|
+
}
|
|
596
|
+
notifyMessages() {
|
|
597
|
+
const e = this.messages;
|
|
598
|
+
for (const t of this.messageListeners)
|
|
599
|
+
t(e);
|
|
600
|
+
}
|
|
601
|
+
setLoading(e) {
|
|
602
|
+
this._isLoading = e;
|
|
603
|
+
for (const t of this.loadingListeners)
|
|
604
|
+
t(e);
|
|
605
|
+
}
|
|
606
|
+
addMessage(e) {
|
|
607
|
+
var t, s;
|
|
608
|
+
this._messages = [...this._messages, e], this.notifyMessages(), (s = (t = this.callbacks).onMessage) == null || s.call(t, e);
|
|
609
|
+
}
|
|
610
|
+
updateMessage(e, t) {
|
|
611
|
+
var s, r;
|
|
612
|
+
this._messages = this._messages.map(
|
|
613
|
+
(i) => i.id === e ? { ...i, ...t } : i
|
|
614
|
+
), this.notifyMessages(), (r = (s = this.callbacks).onMessageUpdate) == null || r.call(s, e, t);
|
|
615
|
+
}
|
|
616
|
+
generateMessageId(e) {
|
|
617
|
+
return `${e}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
618
|
+
}
|
|
619
|
+
// ============================================================================
|
|
620
|
+
// Conversations
|
|
621
|
+
// ============================================================================
|
|
622
|
+
/**
|
|
623
|
+
* Get current conversation ID
|
|
624
|
+
*/
|
|
625
|
+
get conversationId() {
|
|
626
|
+
return this.conversations.getCurrentId();
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Set current conversation ID
|
|
630
|
+
*/
|
|
631
|
+
set conversationId(e) {
|
|
632
|
+
this.conversations.setCurrentId(e);
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Get list of conversations for verified user
|
|
636
|
+
*/
|
|
637
|
+
async getConversations() {
|
|
638
|
+
const e = this.identity.getToken();
|
|
639
|
+
return e ? this.conversations.getConversations(e) : (console.warn("[Crow] Cannot get conversations: user not identified"), []);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Load conversation history
|
|
643
|
+
*/
|
|
644
|
+
async loadHistory(e) {
|
|
645
|
+
const t = this.identity.getToken();
|
|
646
|
+
return t ? this.conversations.loadHistory(e, t) : this.conversations.loadAnonymousHistory(e);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Switch to a different conversation
|
|
650
|
+
*/
|
|
651
|
+
async switchConversation(e) {
|
|
652
|
+
const t = await this.loadHistory(e);
|
|
653
|
+
this.conversations.setCurrentId(e), this.loadMessages(t);
|
|
654
|
+
}
|
|
655
|
+
// ============================================================================
|
|
656
|
+
// Messaging
|
|
657
|
+
// ============================================================================
|
|
658
|
+
/**
|
|
659
|
+
* Send a message and receive streaming response
|
|
660
|
+
* Returns an async generator of stream events
|
|
661
|
+
*/
|
|
662
|
+
async *sendMessage(e) {
|
|
663
|
+
var l, d, h, u, c, g, f, m, p, y;
|
|
664
|
+
if (!e.trim())
|
|
665
|
+
return;
|
|
666
|
+
const t = this.generateMessageId("user");
|
|
667
|
+
this.addMessage({
|
|
668
|
+
id: t,
|
|
669
|
+
content: e,
|
|
670
|
+
role: "user",
|
|
671
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
672
|
+
});
|
|
673
|
+
const s = this.generateMessageId("assistant");
|
|
674
|
+
this.addMessage({
|
|
675
|
+
id: s,
|
|
676
|
+
content: "",
|
|
677
|
+
role: "assistant",
|
|
678
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
679
|
+
}), this.setLoading(!0), this.abortController = new AbortController();
|
|
680
|
+
let r = "", i = "";
|
|
681
|
+
try {
|
|
682
|
+
const a = await fetch(`${this.config.apiUrl}/api/chat/message`, {
|
|
683
|
+
method: "POST",
|
|
684
|
+
headers: { "Content-Type": "application/json" },
|
|
685
|
+
body: JSON.stringify({
|
|
686
|
+
product_id: this.config.productId,
|
|
687
|
+
message: e,
|
|
688
|
+
conversation_id: this.conversations.getCurrentId(),
|
|
689
|
+
identity_token: this.identity.getToken(),
|
|
690
|
+
model: this.config.model,
|
|
691
|
+
subdomain: this.config.subdomain,
|
|
692
|
+
context: Object.keys(this.context).length > 0 ? this.context : void 0
|
|
693
|
+
}),
|
|
694
|
+
signal: this.abortController.signal
|
|
695
|
+
});
|
|
696
|
+
if (!a.ok)
|
|
697
|
+
throw new Error(`HTTP error: ${a.status}`);
|
|
698
|
+
for await (const n of this.readStream(a, this.abortController.signal)) {
|
|
699
|
+
switch (n.type) {
|
|
700
|
+
case "content":
|
|
701
|
+
r = n.accumulated, this.updateMessage(s, { content: r });
|
|
702
|
+
break;
|
|
703
|
+
case "thinking":
|
|
704
|
+
i += n.content, this.updateMessage(s, { thinking: i });
|
|
705
|
+
break;
|
|
706
|
+
case "thinking_complete":
|
|
707
|
+
this.updateMessage(s, { thinkingComplete: !0 });
|
|
708
|
+
break;
|
|
709
|
+
case "citations":
|
|
710
|
+
this.updateMessage(s, { citations: n.citations });
|
|
711
|
+
break;
|
|
712
|
+
case "verification_status":
|
|
713
|
+
this.identity.setVerified(n.isVerified);
|
|
714
|
+
break;
|
|
715
|
+
case "conversation_id":
|
|
716
|
+
this.conversations.setCurrentId(n.conversationId);
|
|
717
|
+
break;
|
|
718
|
+
case "client_tool_call":
|
|
719
|
+
await this.tools.execute(n.toolName, n.arguments), (d = (l = this.callbacks).onToolCall) == null || d.call(l, n);
|
|
720
|
+
break;
|
|
721
|
+
case "tool_call_start":
|
|
722
|
+
case "tool_call_complete":
|
|
723
|
+
(u = (h = this.callbacks).onToolCall) == null || u.call(h, n);
|
|
724
|
+
break;
|
|
725
|
+
case "workflow_started":
|
|
726
|
+
case "workflow_todo_updated":
|
|
727
|
+
case "workflow_ended":
|
|
728
|
+
case "workflow_complete_prompt":
|
|
729
|
+
(g = (c = this.callbacks).onWorkflow) == null || g.call(c, n);
|
|
730
|
+
break;
|
|
731
|
+
case "error":
|
|
732
|
+
this.updateMessage(s, { content: n.message }), (m = (f = this.callbacks).onError) == null || m.call(f, new Error(n.message));
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
yield n;
|
|
736
|
+
}
|
|
737
|
+
} catch (a) {
|
|
738
|
+
if (a instanceof Error && a.name === "AbortError") {
|
|
739
|
+
r ? this.updateMessage(s, { content: r }) : (this._messages = this._messages.filter((n) => n.id !== s), this.notifyMessages());
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
console.error("[Crow] Error:", a), this.updateMessage(s, {
|
|
743
|
+
content: "Sorry, I encountered an error. Please try again."
|
|
744
|
+
}), (y = (p = this.callbacks).onError) == null || y.call(p, a instanceof Error ? a : new Error(String(a)));
|
|
745
|
+
} finally {
|
|
746
|
+
this.setLoading(!1), this.abortController = null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Send a message and wait for complete response (non-streaming)
|
|
751
|
+
*/
|
|
752
|
+
async send(e) {
|
|
753
|
+
let t = null;
|
|
754
|
+
for await (const r of this.sendMessage(e))
|
|
755
|
+
if (r.type === "done")
|
|
756
|
+
break;
|
|
757
|
+
const s = this.messages;
|
|
758
|
+
return s.length > 0 && (t = s[s.length - 1], t.role === "assistant") ? t : null;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Stop current generation
|
|
762
|
+
*/
|
|
763
|
+
stop() {
|
|
764
|
+
this.abortController && (this.abortController.abort(), this.setLoading(!1));
|
|
765
|
+
}
|
|
766
|
+
// ============================================================================
|
|
767
|
+
// Cleanup
|
|
768
|
+
// ============================================================================
|
|
769
|
+
/**
|
|
770
|
+
* Destroy the client and clean up resources
|
|
771
|
+
*/
|
|
772
|
+
destroy() {
|
|
773
|
+
this.stop(), this.messageListeners.clear(), this.loadingListeners.clear();
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
export {
|
|
777
|
+
S as ConversationManager,
|
|
778
|
+
A as CrowClient,
|
|
779
|
+
_ as DEFAULT_TOOLS,
|
|
780
|
+
w as DEFAULT_TOOL_NAMES,
|
|
781
|
+
I as IdentityManager,
|
|
782
|
+
C as ToolManager,
|
|
783
|
+
x as createLocalStorageAdapter,
|
|
784
|
+
k as createMemoryStorageAdapter,
|
|
785
|
+
T as parseSSEChunk,
|
|
786
|
+
M as parseSSEData,
|
|
787
|
+
$ as streamResponse
|
|
788
|
+
};
|