@usecrow/client 0.1.24 → 0.1.26
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/PageController-GcMFZYwU.cjs +9 -0
- package/dist/PageController-KoeqDxMP.js +1377 -0
- package/dist/SimulatorMask-CgaoHOve.js +120 -0
- package/dist/SimulatorMask-DoBLczoQ.cjs +2 -0
- package/dist/browser.cjs +1 -284
- package/dist/browser.d.ts +342 -52
- package/dist/browser.js +18 -276
- package/dist/browserUse-BOc9kyBK.cjs +1 -0
- package/dist/browserUse-BbPG4pH1.js +205 -0
- package/dist/index.cjs +3 -1107
- package/dist/index.d.ts +474 -311
- package/dist/index.js +254 -614
- package/package.json +8 -6
- package/dist/browser.cjs.map +0 -1
- package/dist/browser.d.cts +0 -52
- package/dist/browser.js.map +0 -1
- package/dist/browserUse-CZNpayEF.d.cts +0 -188
- package/dist/browserUse-CZNpayEF.d.ts +0 -188
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -311
- package/dist/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,1107 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var IdentityManager = class {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.state = {
|
|
7
|
-
token: null,
|
|
8
|
-
metadata: {},
|
|
9
|
-
isVerified: false
|
|
10
|
-
};
|
|
11
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Identify the current user with a JWT token
|
|
15
|
-
*/
|
|
16
|
-
identify(options) {
|
|
17
|
-
const { token, ...metadata } = options;
|
|
18
|
-
this.state = {
|
|
19
|
-
token,
|
|
20
|
-
metadata,
|
|
21
|
-
isVerified: false
|
|
22
|
-
// Will be set when server confirms
|
|
23
|
-
};
|
|
24
|
-
this.notify();
|
|
25
|
-
console.log("[Crow] User identified");
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Update verification status (called when server confirms)
|
|
29
|
-
*/
|
|
30
|
-
setVerified(isVerified) {
|
|
31
|
-
this.state = { ...this.state, isVerified };
|
|
32
|
-
this.notify();
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Reset user identity (call on logout)
|
|
36
|
-
*/
|
|
37
|
-
reset() {
|
|
38
|
-
this.state = {
|
|
39
|
-
token: null,
|
|
40
|
-
metadata: {},
|
|
41
|
-
isVerified: false
|
|
42
|
-
};
|
|
43
|
-
this.notify();
|
|
44
|
-
console.log("[Crow] User reset");
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Get current identity token
|
|
48
|
-
*/
|
|
49
|
-
getToken() {
|
|
50
|
-
return this.state.token;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Get current identity state
|
|
54
|
-
*/
|
|
55
|
-
getState() {
|
|
56
|
-
return { ...this.state };
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Check if user is identified
|
|
60
|
-
*/
|
|
61
|
-
isIdentified() {
|
|
62
|
-
return this.state.token !== null;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Check if user is verified
|
|
66
|
-
*/
|
|
67
|
-
isVerified() {
|
|
68
|
-
return this.state.isVerified;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Subscribe to identity changes
|
|
72
|
-
*/
|
|
73
|
-
subscribe(callback) {
|
|
74
|
-
this.listeners.add(callback);
|
|
75
|
-
return () => this.listeners.delete(callback);
|
|
76
|
-
}
|
|
77
|
-
notify() {
|
|
78
|
-
const state = this.getState();
|
|
79
|
-
for (const listener of this.listeners) {
|
|
80
|
-
listener(state);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// src/tools.ts
|
|
86
|
-
var ToolManager = class {
|
|
87
|
-
constructor() {
|
|
88
|
-
this.handlers = {};
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Register client-side tool handlers
|
|
92
|
-
*/
|
|
93
|
-
register(tools) {
|
|
94
|
-
for (const [name, handler] of Object.entries(tools)) {
|
|
95
|
-
if (typeof handler === "function") {
|
|
96
|
-
this.handlers[name] = handler;
|
|
97
|
-
console.log(`[Crow] Registered client tool: ${name}`);
|
|
98
|
-
} else {
|
|
99
|
-
console.warn(`[Crow] Skipping ${name}: handler is not a function`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Unregister a tool handler
|
|
105
|
-
*/
|
|
106
|
-
unregister(name) {
|
|
107
|
-
delete this.handlers[name];
|
|
108
|
-
console.log(`[Crow] Unregistered client tool: ${name}`);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Check if a tool is registered
|
|
112
|
-
*/
|
|
113
|
-
has(name) {
|
|
114
|
-
return name in this.handlers;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Get all registered tool names
|
|
118
|
-
*/
|
|
119
|
-
getRegisteredTools() {
|
|
120
|
-
return Object.keys(this.handlers);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Execute a client-side tool
|
|
124
|
-
*/
|
|
125
|
-
async execute(name, args) {
|
|
126
|
-
const handler = this.handlers[name];
|
|
127
|
-
if (!handler) {
|
|
128
|
-
console.warn(`[Crow] No handler registered for tool: ${name}`);
|
|
129
|
-
return {
|
|
130
|
-
status: "error",
|
|
131
|
-
error: `No handler registered for tool: ${name}`
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
console.log(`[Crow] Executing client tool: ${name}`, args);
|
|
136
|
-
const result = await handler(args);
|
|
137
|
-
console.log(`[Crow] Tool ${name} completed:`, result);
|
|
138
|
-
return result;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
141
|
-
console.error(`[Crow] Tool ${name} failed:`, error);
|
|
142
|
-
return {
|
|
143
|
-
status: "error",
|
|
144
|
-
error: errorMessage
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// src/conversations.ts
|
|
151
|
-
var STORAGE_KEY_PREFIX = "crow_conv_";
|
|
152
|
-
var ConversationManager = class {
|
|
153
|
-
constructor(productId, apiUrl) {
|
|
154
|
-
this.currentId = null;
|
|
155
|
-
this.productId = productId;
|
|
156
|
-
this.apiUrl = apiUrl;
|
|
157
|
-
this.currentId = this.loadFromStorage();
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Get localStorage key for this product
|
|
161
|
-
*/
|
|
162
|
-
getStorageKey() {
|
|
163
|
-
return `${STORAGE_KEY_PREFIX}${this.productId}`;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Load conversation ID from localStorage
|
|
167
|
-
*/
|
|
168
|
-
loadFromStorage() {
|
|
169
|
-
try {
|
|
170
|
-
return localStorage.getItem(this.getStorageKey());
|
|
171
|
-
} catch {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Save conversation ID to localStorage
|
|
177
|
-
*/
|
|
178
|
-
saveToStorage(id) {
|
|
179
|
-
try {
|
|
180
|
-
localStorage.setItem(this.getStorageKey(), id);
|
|
181
|
-
} catch {
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Clear conversation ID from localStorage
|
|
186
|
-
*/
|
|
187
|
-
clearStorage() {
|
|
188
|
-
try {
|
|
189
|
-
localStorage.removeItem(this.getStorageKey());
|
|
190
|
-
} catch {
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Get current conversation ID
|
|
195
|
-
*/
|
|
196
|
-
getCurrentId() {
|
|
197
|
-
return this.currentId;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Set current conversation ID
|
|
201
|
-
*/
|
|
202
|
-
setCurrentId(id) {
|
|
203
|
-
this.currentId = id;
|
|
204
|
-
if (id) {
|
|
205
|
-
this.saveToStorage(id);
|
|
206
|
-
} else {
|
|
207
|
-
this.clearStorage();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Check if there's a restored conversation
|
|
212
|
-
*/
|
|
213
|
-
hasRestoredConversation() {
|
|
214
|
-
return this.currentId !== null;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Clear current conversation (start new)
|
|
218
|
-
*/
|
|
219
|
-
clear() {
|
|
220
|
-
this.currentId = null;
|
|
221
|
-
this.clearStorage();
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Fetch list of conversations for verified user
|
|
225
|
-
*/
|
|
226
|
-
async getConversations(identityToken) {
|
|
227
|
-
try {
|
|
228
|
-
const response = await fetch(
|
|
229
|
-
`${this.apiUrl}/api/chat/conversations?product_id=${this.productId}&identity_token=${encodeURIComponent(identityToken)}`
|
|
230
|
-
);
|
|
231
|
-
if (!response.ok) {
|
|
232
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
233
|
-
}
|
|
234
|
-
const data = await response.json();
|
|
235
|
-
return data.conversations || [];
|
|
236
|
-
} catch (error) {
|
|
237
|
-
console.error("[Crow] Failed to load conversations:", error);
|
|
238
|
-
return [];
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Load conversation history for verified user
|
|
243
|
-
*/
|
|
244
|
-
async loadHistory(conversationId, identityToken) {
|
|
245
|
-
try {
|
|
246
|
-
const response = await fetch(
|
|
247
|
-
`${this.apiUrl}/api/chat/conversations/${conversationId}/history?product_id=${this.productId}&identity_token=${encodeURIComponent(identityToken)}`
|
|
248
|
-
);
|
|
249
|
-
if (!response.ok) {
|
|
250
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
251
|
-
}
|
|
252
|
-
const data = await response.json();
|
|
253
|
-
return this.parseHistoryMessages(data.messages || []);
|
|
254
|
-
} catch (error) {
|
|
255
|
-
console.error("[Crow] Failed to load conversation history:", error);
|
|
256
|
-
return [];
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Load conversation history for anonymous user
|
|
261
|
-
*/
|
|
262
|
-
async loadAnonymousHistory(conversationId) {
|
|
263
|
-
try {
|
|
264
|
-
const response = await fetch(
|
|
265
|
-
`${this.apiUrl}/api/chat/conversations/${conversationId}/history/anonymous?product_id=${this.productId}`
|
|
266
|
-
);
|
|
267
|
-
if (!response.ok) {
|
|
268
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
269
|
-
}
|
|
270
|
-
const data = await response.json();
|
|
271
|
-
return this.parseHistoryMessages(data.messages || []);
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.error("[Crow] Failed to load anonymous conversation history:", error);
|
|
274
|
-
return [];
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Parse history messages from API format
|
|
279
|
-
*/
|
|
280
|
-
parseHistoryMessages(messages) {
|
|
281
|
-
return messages.filter((msg) => msg.role !== "tool" && !msg.content.startsWith("[Client Tool Result:")).map((msg, idx) => ({
|
|
282
|
-
id: `history-${idx}`,
|
|
283
|
-
content: this.parseContent(msg.content),
|
|
284
|
-
role: msg.role === "assistant" ? "assistant" : "user",
|
|
285
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
286
|
-
}));
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Parse structured content (with thinking/text blocks) and extract just text
|
|
290
|
-
*/
|
|
291
|
-
parseContent(content) {
|
|
292
|
-
try {
|
|
293
|
-
const parsed = JSON.parse(content);
|
|
294
|
-
if (Array.isArray(parsed)) {
|
|
295
|
-
const textBlock = parsed.find(
|
|
296
|
-
(b) => b.type === "text"
|
|
297
|
-
);
|
|
298
|
-
return textBlock?.text || content;
|
|
299
|
-
}
|
|
300
|
-
} catch {
|
|
301
|
-
}
|
|
302
|
-
if (content.includes("'type': 'text'")) {
|
|
303
|
-
const textBlockMatch = content.match(
|
|
304
|
-
/\{'text':\s*'((?:[^'\\]|\\.)*)'\s*,\s*'type':\s*'text'/
|
|
305
|
-
);
|
|
306
|
-
if (textBlockMatch) {
|
|
307
|
-
return textBlockMatch[1].replace(/\\n/g, "\n").replace(/\\'/g, "'");
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return content;
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
// src/streaming.ts
|
|
315
|
-
function parseSSEData(data) {
|
|
316
|
-
if (data === "[DONE]") {
|
|
317
|
-
return { type: "done" };
|
|
318
|
-
}
|
|
319
|
-
try {
|
|
320
|
-
const parsed = JSON.parse(data);
|
|
321
|
-
switch (parsed.type) {
|
|
322
|
-
case "verification_status":
|
|
323
|
-
return {
|
|
324
|
-
type: "verification_status",
|
|
325
|
-
isVerified: parsed.is_verified === true
|
|
326
|
-
};
|
|
327
|
-
case "conversation_id":
|
|
328
|
-
return {
|
|
329
|
-
type: "conversation_id",
|
|
330
|
-
conversationId: parsed.conversation_id
|
|
331
|
-
};
|
|
332
|
-
case "thinking":
|
|
333
|
-
if (parsed.status === "complete") {
|
|
334
|
-
return { type: "thinking_complete" };
|
|
335
|
-
}
|
|
336
|
-
return null;
|
|
337
|
-
case "thinking_token":
|
|
338
|
-
return {
|
|
339
|
-
type: "thinking",
|
|
340
|
-
content: parsed.content || ""
|
|
341
|
-
};
|
|
342
|
-
case "content":
|
|
343
|
-
return {
|
|
344
|
-
type: "content",
|
|
345
|
-
text: parsed.content || "",
|
|
346
|
-
accumulated: ""
|
|
347
|
-
// Will be set by caller
|
|
348
|
-
};
|
|
349
|
-
case "citations":
|
|
350
|
-
return {
|
|
351
|
-
type: "citations",
|
|
352
|
-
citations: parsed.citations
|
|
353
|
-
};
|
|
354
|
-
case "error":
|
|
355
|
-
return {
|
|
356
|
-
type: "error",
|
|
357
|
-
message: parsed.message || "Unknown error"
|
|
358
|
-
};
|
|
359
|
-
case "tool_call_start":
|
|
360
|
-
return {
|
|
361
|
-
type: "tool_call_start",
|
|
362
|
-
toolName: parsed.tool_name,
|
|
363
|
-
arguments: parsed.arguments || {}
|
|
364
|
-
};
|
|
365
|
-
case "tool_call_complete":
|
|
366
|
-
return {
|
|
367
|
-
type: "tool_call_complete",
|
|
368
|
-
toolName: parsed.tool_name,
|
|
369
|
-
success: parsed.success
|
|
370
|
-
};
|
|
371
|
-
case "client_tool_call":
|
|
372
|
-
return {
|
|
373
|
-
type: "client_tool_call",
|
|
374
|
-
toolName: parsed.tool_name,
|
|
375
|
-
arguments: parsed.arguments || {}
|
|
376
|
-
};
|
|
377
|
-
case "workflow_started":
|
|
378
|
-
return {
|
|
379
|
-
type: "workflow_started",
|
|
380
|
-
name: parsed.name,
|
|
381
|
-
todos: parsed.todos
|
|
382
|
-
};
|
|
383
|
-
case "todo_updated":
|
|
384
|
-
return {
|
|
385
|
-
type: "workflow_todo_updated",
|
|
386
|
-
todoId: parsed.id,
|
|
387
|
-
status: parsed.status
|
|
388
|
-
};
|
|
389
|
-
case "workflow_ended":
|
|
390
|
-
return { type: "workflow_ended" };
|
|
391
|
-
case "workflow_complete_prompt":
|
|
392
|
-
return { type: "workflow_complete_prompt" };
|
|
393
|
-
default:
|
|
394
|
-
return null;
|
|
395
|
-
}
|
|
396
|
-
} catch {
|
|
397
|
-
console.error("[Crow] Failed to parse SSE data:", data);
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
function* parseSSEChunk(chunk) {
|
|
402
|
-
const lines = chunk.split("\n");
|
|
403
|
-
for (const line of lines) {
|
|
404
|
-
if (line.startsWith("data: ")) {
|
|
405
|
-
yield line.slice(6).trim();
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
async function* streamResponse(response, signal) {
|
|
410
|
-
const reader = response.body?.getReader();
|
|
411
|
-
if (!reader) {
|
|
412
|
-
throw new Error("Response body is not readable");
|
|
413
|
-
}
|
|
414
|
-
const decoder = new TextDecoder();
|
|
415
|
-
let accumulatedContent = "";
|
|
416
|
-
try {
|
|
417
|
-
while (true) {
|
|
418
|
-
if (signal?.aborted) {
|
|
419
|
-
reader.cancel();
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const { done, value } = await reader.read();
|
|
423
|
-
if (done) break;
|
|
424
|
-
const chunk = decoder.decode(value);
|
|
425
|
-
for (const data of parseSSEChunk(chunk)) {
|
|
426
|
-
const event = parseSSEData(data);
|
|
427
|
-
if (event) {
|
|
428
|
-
if (event.type === "content") {
|
|
429
|
-
accumulatedContent += event.text;
|
|
430
|
-
yield { ...event, accumulated: accumulatedContent };
|
|
431
|
-
} else {
|
|
432
|
-
yield event;
|
|
433
|
-
}
|
|
434
|
-
if (event.type === "done") {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
} finally {
|
|
441
|
-
reader.releaseLock();
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// src/defaultTools.ts
|
|
446
|
-
var DEFAULT_TOOLS = {
|
|
447
|
-
/**
|
|
448
|
-
* Refresh the current page in the user's browser
|
|
449
|
-
*/
|
|
450
|
-
refreshPage: async () => {
|
|
451
|
-
try {
|
|
452
|
-
window.location.reload();
|
|
453
|
-
return { status: "success", data: { message: "Page refresh initiated" } };
|
|
454
|
-
} catch (error) {
|
|
455
|
-
return { status: "error", error: String(error) };
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
var DEFAULT_TOOL_NAMES = Object.keys(DEFAULT_TOOLS);
|
|
460
|
-
|
|
461
|
-
// src/CrowClient.ts
|
|
462
|
-
var DEFAULT_API_URL = "https://api.usecrow.org";
|
|
463
|
-
var DEFAULT_MODEL = "claude-sonnet-4-20250514";
|
|
464
|
-
var CrowClient = class {
|
|
465
|
-
constructor(config) {
|
|
466
|
-
this.context = {};
|
|
467
|
-
this.abortController = null;
|
|
468
|
-
this.callbacks = {};
|
|
469
|
-
// Message state
|
|
470
|
-
this._messages = [];
|
|
471
|
-
this.messageListeners = /* @__PURE__ */ new Set();
|
|
472
|
-
// Loading state
|
|
473
|
-
this._isLoading = false;
|
|
474
|
-
this.loadingListeners = /* @__PURE__ */ new Set();
|
|
475
|
-
this.config = {
|
|
476
|
-
productId: config.productId,
|
|
477
|
-
apiUrl: config.apiUrl || DEFAULT_API_URL,
|
|
478
|
-
model: config.model || DEFAULT_MODEL
|
|
479
|
-
};
|
|
480
|
-
this.identity = new IdentityManager();
|
|
481
|
-
this.tools = new ToolManager();
|
|
482
|
-
this.conversations = new ConversationManager(
|
|
483
|
-
this.config.productId,
|
|
484
|
-
this.config.apiUrl
|
|
485
|
-
);
|
|
486
|
-
this.tools.register(DEFAULT_TOOLS);
|
|
487
|
-
console.log("[Crow] Default tools registered:", DEFAULT_TOOL_NAMES.join(", "));
|
|
488
|
-
this.identity.subscribe((state) => {
|
|
489
|
-
this.callbacks.onVerificationStatus?.(state.isVerified);
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
// ============================================================================
|
|
493
|
-
// Configuration
|
|
494
|
-
// ============================================================================
|
|
495
|
-
/**
|
|
496
|
-
* Get current product ID
|
|
497
|
-
*/
|
|
498
|
-
get productId() {
|
|
499
|
-
return this.config.productId;
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Get current API URL
|
|
503
|
-
*/
|
|
504
|
-
get apiUrl() {
|
|
505
|
-
return this.config.apiUrl;
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
|
-
* Get/set current model
|
|
509
|
-
*/
|
|
510
|
-
get model() {
|
|
511
|
-
return this.config.model;
|
|
512
|
-
}
|
|
513
|
-
set model(value) {
|
|
514
|
-
this.config.model = value;
|
|
515
|
-
}
|
|
516
|
-
// ============================================================================
|
|
517
|
-
// Event Callbacks
|
|
518
|
-
// ============================================================================
|
|
519
|
-
/**
|
|
520
|
-
* Register event callbacks
|
|
521
|
-
*/
|
|
522
|
-
on(callbacks) {
|
|
523
|
-
this.callbacks = { ...this.callbacks, ...callbacks };
|
|
524
|
-
}
|
|
525
|
-
// ============================================================================
|
|
526
|
-
// Identity
|
|
527
|
-
// ============================================================================
|
|
528
|
-
/**
|
|
529
|
-
* Identify the current user with a JWT token
|
|
530
|
-
*/
|
|
531
|
-
identify(options) {
|
|
532
|
-
this.identity.identify(options);
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Reset user identity (call on logout)
|
|
536
|
-
*/
|
|
537
|
-
resetUser() {
|
|
538
|
-
this.identity.reset();
|
|
539
|
-
this.clearMessages();
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Check if user is identified
|
|
543
|
-
*/
|
|
544
|
-
isIdentified() {
|
|
545
|
-
return this.identity.isIdentified();
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Check if user is verified by server
|
|
549
|
-
*/
|
|
550
|
-
isVerified() {
|
|
551
|
-
return this.identity.isVerified();
|
|
552
|
-
}
|
|
553
|
-
// ============================================================================
|
|
554
|
-
// Tools
|
|
555
|
-
// ============================================================================
|
|
556
|
-
/**
|
|
557
|
-
* Register client-side tool handlers
|
|
558
|
-
*/
|
|
559
|
-
registerTools(tools) {
|
|
560
|
-
this.tools.register(tools);
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Unregister a tool handler
|
|
564
|
-
*/
|
|
565
|
-
unregisterTool(name) {
|
|
566
|
-
this.tools.unregister(name);
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* Get list of registered tool names
|
|
570
|
-
*/
|
|
571
|
-
getRegisteredTools() {
|
|
572
|
-
return this.tools.getRegisteredTools();
|
|
573
|
-
}
|
|
574
|
-
// ============================================================================
|
|
575
|
-
// Context
|
|
576
|
-
// ============================================================================
|
|
577
|
-
/**
|
|
578
|
-
* Set context data to be sent with messages
|
|
579
|
-
*/
|
|
580
|
-
setContext(data) {
|
|
581
|
-
this.context = { ...this.context, ...data };
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Clear context data
|
|
585
|
-
*/
|
|
586
|
-
clearContext() {
|
|
587
|
-
this.context = {};
|
|
588
|
-
}
|
|
589
|
-
// ============================================================================
|
|
590
|
-
// Messages
|
|
591
|
-
// ============================================================================
|
|
592
|
-
/**
|
|
593
|
-
* Get current messages
|
|
594
|
-
*/
|
|
595
|
-
get messages() {
|
|
596
|
-
return [...this._messages];
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* Check if currently loading/streaming
|
|
600
|
-
*/
|
|
601
|
-
get isLoading() {
|
|
602
|
-
return this._isLoading;
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* Subscribe to message changes
|
|
606
|
-
*/
|
|
607
|
-
onMessages(callback) {
|
|
608
|
-
this.messageListeners.add(callback);
|
|
609
|
-
return () => this.messageListeners.delete(callback);
|
|
610
|
-
}
|
|
611
|
-
/**
|
|
612
|
-
* Subscribe to loading state changes
|
|
613
|
-
*/
|
|
614
|
-
onLoading(callback) {
|
|
615
|
-
this.loadingListeners.add(callback);
|
|
616
|
-
return () => this.loadingListeners.delete(callback);
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Clear all messages and start new conversation
|
|
620
|
-
*/
|
|
621
|
-
clearMessages() {
|
|
622
|
-
this._messages = [];
|
|
623
|
-
this.conversations.clear();
|
|
624
|
-
this.notifyMessages();
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Load messages from history
|
|
628
|
-
*/
|
|
629
|
-
loadMessages(messages) {
|
|
630
|
-
this._messages = messages;
|
|
631
|
-
this.notifyMessages();
|
|
632
|
-
}
|
|
633
|
-
notifyMessages() {
|
|
634
|
-
const messages = this.messages;
|
|
635
|
-
for (const listener of this.messageListeners) {
|
|
636
|
-
listener(messages);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
setLoading(isLoading) {
|
|
640
|
-
this._isLoading = isLoading;
|
|
641
|
-
for (const listener of this.loadingListeners) {
|
|
642
|
-
listener(isLoading);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
addMessage(message) {
|
|
646
|
-
this._messages = [...this._messages, message];
|
|
647
|
-
this.notifyMessages();
|
|
648
|
-
this.callbacks.onMessage?.(message);
|
|
649
|
-
}
|
|
650
|
-
updateMessage(id, updates) {
|
|
651
|
-
this._messages = this._messages.map(
|
|
652
|
-
(msg) => msg.id === id ? { ...msg, ...updates } : msg
|
|
653
|
-
);
|
|
654
|
-
this.notifyMessages();
|
|
655
|
-
this.callbacks.onMessageUpdate?.(id, updates);
|
|
656
|
-
}
|
|
657
|
-
generateMessageId(prefix) {
|
|
658
|
-
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
659
|
-
}
|
|
660
|
-
// ============================================================================
|
|
661
|
-
// Conversations
|
|
662
|
-
// ============================================================================
|
|
663
|
-
/**
|
|
664
|
-
* Get current conversation ID
|
|
665
|
-
*/
|
|
666
|
-
get conversationId() {
|
|
667
|
-
return this.conversations.getCurrentId();
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* Set current conversation ID
|
|
671
|
-
*/
|
|
672
|
-
set conversationId(id) {
|
|
673
|
-
this.conversations.setCurrentId(id);
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Get list of conversations for verified user
|
|
677
|
-
*/
|
|
678
|
-
async getConversations() {
|
|
679
|
-
const token = this.identity.getToken();
|
|
680
|
-
if (!token) {
|
|
681
|
-
console.warn("[Crow] Cannot get conversations: user not identified");
|
|
682
|
-
return [];
|
|
683
|
-
}
|
|
684
|
-
return this.conversations.getConversations(token);
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Load conversation history
|
|
688
|
-
*/
|
|
689
|
-
async loadHistory(conversationId) {
|
|
690
|
-
const token = this.identity.getToken();
|
|
691
|
-
if (token) {
|
|
692
|
-
return this.conversations.loadHistory(conversationId, token);
|
|
693
|
-
}
|
|
694
|
-
return this.conversations.loadAnonymousHistory(conversationId);
|
|
695
|
-
}
|
|
696
|
-
/**
|
|
697
|
-
* Switch to a different conversation
|
|
698
|
-
*/
|
|
699
|
-
async switchConversation(conversationId) {
|
|
700
|
-
const messages = await this.loadHistory(conversationId);
|
|
701
|
-
this.conversations.setCurrentId(conversationId);
|
|
702
|
-
this.loadMessages(messages);
|
|
703
|
-
}
|
|
704
|
-
// ============================================================================
|
|
705
|
-
// Messaging
|
|
706
|
-
// ============================================================================
|
|
707
|
-
/**
|
|
708
|
-
* Send a message and receive streaming response
|
|
709
|
-
* Returns an async generator of stream events
|
|
710
|
-
*/
|
|
711
|
-
async *sendMessage(content) {
|
|
712
|
-
if (!content.trim()) {
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
const userMsgId = this.generateMessageId("user");
|
|
716
|
-
this.addMessage({
|
|
717
|
-
id: userMsgId,
|
|
718
|
-
content,
|
|
719
|
-
role: "user",
|
|
720
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
721
|
-
});
|
|
722
|
-
const botMsgId = this.generateMessageId("assistant");
|
|
723
|
-
this.addMessage({
|
|
724
|
-
id: botMsgId,
|
|
725
|
-
content: "",
|
|
726
|
-
role: "assistant",
|
|
727
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
728
|
-
});
|
|
729
|
-
this.setLoading(true);
|
|
730
|
-
this.abortController = new AbortController();
|
|
731
|
-
let accumulatedContent = "";
|
|
732
|
-
let accumulatedThinking = "";
|
|
733
|
-
try {
|
|
734
|
-
const response = await fetch(`${this.config.apiUrl}/api/chat/message`, {
|
|
735
|
-
method: "POST",
|
|
736
|
-
headers: { "Content-Type": "application/json" },
|
|
737
|
-
body: JSON.stringify({
|
|
738
|
-
product_id: this.config.productId,
|
|
739
|
-
message: content,
|
|
740
|
-
conversation_id: this.conversations.getCurrentId(),
|
|
741
|
-
identity_token: this.identity.getToken(),
|
|
742
|
-
model: this.config.model,
|
|
743
|
-
context: Object.keys(this.context).length > 0 ? this.context : void 0
|
|
744
|
-
}),
|
|
745
|
-
signal: this.abortController.signal
|
|
746
|
-
});
|
|
747
|
-
if (!response.ok) {
|
|
748
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
749
|
-
}
|
|
750
|
-
for await (const event of streamResponse(response, this.abortController.signal)) {
|
|
751
|
-
switch (event.type) {
|
|
752
|
-
case "content":
|
|
753
|
-
accumulatedContent = event.accumulated;
|
|
754
|
-
this.updateMessage(botMsgId, { content: accumulatedContent });
|
|
755
|
-
break;
|
|
756
|
-
case "thinking":
|
|
757
|
-
accumulatedThinking += event.content;
|
|
758
|
-
this.updateMessage(botMsgId, { thinking: accumulatedThinking });
|
|
759
|
-
break;
|
|
760
|
-
case "thinking_complete":
|
|
761
|
-
this.updateMessage(botMsgId, { thinkingComplete: true });
|
|
762
|
-
break;
|
|
763
|
-
case "citations":
|
|
764
|
-
this.updateMessage(botMsgId, { citations: event.citations });
|
|
765
|
-
break;
|
|
766
|
-
case "verification_status":
|
|
767
|
-
this.identity.setVerified(event.isVerified);
|
|
768
|
-
break;
|
|
769
|
-
case "conversation_id":
|
|
770
|
-
this.conversations.setCurrentId(event.conversationId);
|
|
771
|
-
break;
|
|
772
|
-
case "client_tool_call":
|
|
773
|
-
await this.tools.execute(event.toolName, event.arguments);
|
|
774
|
-
this.callbacks.onToolCall?.(event);
|
|
775
|
-
break;
|
|
776
|
-
case "tool_call_start":
|
|
777
|
-
case "tool_call_complete":
|
|
778
|
-
this.callbacks.onToolCall?.(event);
|
|
779
|
-
break;
|
|
780
|
-
case "workflow_started":
|
|
781
|
-
case "workflow_todo_updated":
|
|
782
|
-
case "workflow_ended":
|
|
783
|
-
case "workflow_complete_prompt":
|
|
784
|
-
this.callbacks.onWorkflow?.(event);
|
|
785
|
-
break;
|
|
786
|
-
case "error":
|
|
787
|
-
this.updateMessage(botMsgId, { content: event.message });
|
|
788
|
-
this.callbacks.onError?.(new Error(event.message));
|
|
789
|
-
break;
|
|
790
|
-
}
|
|
791
|
-
yield event;
|
|
792
|
-
}
|
|
793
|
-
} catch (error) {
|
|
794
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
795
|
-
if (accumulatedContent) {
|
|
796
|
-
this.updateMessage(botMsgId, { content: accumulatedContent });
|
|
797
|
-
} else {
|
|
798
|
-
this._messages = this._messages.filter((msg) => msg.id !== botMsgId);
|
|
799
|
-
this.notifyMessages();
|
|
800
|
-
}
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
console.error("[Crow] Error:", error);
|
|
804
|
-
this.updateMessage(botMsgId, {
|
|
805
|
-
content: "Sorry, I encountered an error. Please try again."
|
|
806
|
-
});
|
|
807
|
-
this.callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
808
|
-
} finally {
|
|
809
|
-
this.setLoading(false);
|
|
810
|
-
this.abortController = null;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Send a message and wait for complete response (non-streaming)
|
|
815
|
-
*/
|
|
816
|
-
async send(content) {
|
|
817
|
-
let lastMessage = null;
|
|
818
|
-
for await (const event of this.sendMessage(content)) {
|
|
819
|
-
if (event.type === "done") {
|
|
820
|
-
break;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
const messages = this.messages;
|
|
824
|
-
if (messages.length > 0) {
|
|
825
|
-
lastMessage = messages[messages.length - 1];
|
|
826
|
-
if (lastMessage.role === "assistant") {
|
|
827
|
-
return lastMessage;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
return null;
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* Stop current generation
|
|
834
|
-
*/
|
|
835
|
-
stop() {
|
|
836
|
-
if (this.abortController) {
|
|
837
|
-
this.abortController.abort();
|
|
838
|
-
this.setLoading(false);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
// ============================================================================
|
|
842
|
-
// Cleanup
|
|
843
|
-
// ============================================================================
|
|
844
|
-
/**
|
|
845
|
-
* Destroy the client and clean up resources
|
|
846
|
-
*/
|
|
847
|
-
destroy() {
|
|
848
|
-
this.stop();
|
|
849
|
-
this.messageListeners.clear();
|
|
850
|
-
this.loadingListeners.clear();
|
|
851
|
-
}
|
|
852
|
-
};
|
|
853
|
-
var PageControllerModule = null;
|
|
854
|
-
async function getPageController() {
|
|
855
|
-
if (!PageControllerModule) {
|
|
856
|
-
try {
|
|
857
|
-
PageControllerModule = await import('@page-agent/page-controller');
|
|
858
|
-
} catch (error) {
|
|
859
|
-
throw new Error(
|
|
860
|
-
'PageController not available. Either import from "@usecrow/client/browser" or install @page-agent/page-controller as a dependency.'
|
|
861
|
-
);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
return PageControllerModule.PageController;
|
|
865
|
-
}
|
|
866
|
-
var CrowBrowserUse = class {
|
|
867
|
-
constructor(config) {
|
|
868
|
-
this.pageController = null;
|
|
869
|
-
this.sessionId = null;
|
|
870
|
-
this.maxSteps = 20;
|
|
871
|
-
this.config = config;
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Initialize PageController with non-blocking pointer
|
|
875
|
-
*/
|
|
876
|
-
async initPageController() {
|
|
877
|
-
if (this.pageController) {
|
|
878
|
-
return this.pageController;
|
|
879
|
-
}
|
|
880
|
-
try {
|
|
881
|
-
const PageController = await getPageController();
|
|
882
|
-
this.pageController = new PageController({
|
|
883
|
-
enableMask: true,
|
|
884
|
-
viewportExpansion: 500,
|
|
885
|
-
highlightLabelOpacity: 0,
|
|
886
|
-
// Hide numbered labels from users
|
|
887
|
-
highlightOpacity: 0
|
|
888
|
-
// Hide highlight boxes from users
|
|
889
|
-
});
|
|
890
|
-
await this.pageController.showMask();
|
|
891
|
-
const mask = this.pageController.mask;
|
|
892
|
-
if (mask?.wrapper) {
|
|
893
|
-
mask.wrapper.style.pointerEvents = "none";
|
|
894
|
-
}
|
|
895
|
-
console.log("[CrowBrowserUse] PageController initialized with non-blocking pointer");
|
|
896
|
-
return this.pageController;
|
|
897
|
-
} catch (error) {
|
|
898
|
-
console.error("[CrowBrowserUse] Failed to import @page-agent/page-controller:", error);
|
|
899
|
-
throw new Error(
|
|
900
|
-
"Failed to initialize browser automation. Make sure @page-agent/page-controller is installed."
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
/**
|
|
905
|
-
* Execute a browser automation task
|
|
906
|
-
*/
|
|
907
|
-
async execute(task) {
|
|
908
|
-
console.log("[CrowBrowserUse] Starting task:", task);
|
|
909
|
-
try {
|
|
910
|
-
const controller = await this.initPageController();
|
|
911
|
-
const startResponse = await this.startSession(task);
|
|
912
|
-
this.sessionId = startResponse.session_id;
|
|
913
|
-
this.maxSteps = startResponse.max_steps;
|
|
914
|
-
console.log("[CrowBrowserUse] Session started:", this.sessionId);
|
|
915
|
-
let stepCount = 0;
|
|
916
|
-
let lastActionResult;
|
|
917
|
-
while (stepCount < this.maxSteps) {
|
|
918
|
-
stepCount++;
|
|
919
|
-
const browserState = await controller.getBrowserState();
|
|
920
|
-
const stepResponse = await this.processStep(browserState, lastActionResult);
|
|
921
|
-
if (stepResponse.done) {
|
|
922
|
-
console.log("[CrowBrowserUse] Task completed:", stepResponse.message);
|
|
923
|
-
await this.cleanup();
|
|
924
|
-
return {
|
|
925
|
-
status: stepResponse.success ? "success" : "error",
|
|
926
|
-
data: {
|
|
927
|
-
message: stepResponse.message,
|
|
928
|
-
steps: stepCount
|
|
929
|
-
},
|
|
930
|
-
error: stepResponse.success ? void 0 : stepResponse.message
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
if (stepResponse.error) {
|
|
934
|
-
console.error("[CrowBrowserUse] Error:", stepResponse.error);
|
|
935
|
-
await this.cleanup();
|
|
936
|
-
return {
|
|
937
|
-
status: "error",
|
|
938
|
-
error: stepResponse.error
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
if (stepResponse.action) {
|
|
942
|
-
lastActionResult = await this.executeAction(controller, stepResponse.action);
|
|
943
|
-
console.log(`[CrowBrowserUse] Step ${stepCount}:`, lastActionResult);
|
|
944
|
-
}
|
|
945
|
-
if (stepResponse.reflection) {
|
|
946
|
-
console.log("[CrowBrowserUse] Reflection:", stepResponse.reflection.next_goal);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
await this.cleanup();
|
|
950
|
-
return {
|
|
951
|
-
status: "error",
|
|
952
|
-
error: `Task incomplete after ${this.maxSteps} steps`
|
|
953
|
-
};
|
|
954
|
-
} catch (error) {
|
|
955
|
-
console.error("[CrowBrowserUse] Error:", error);
|
|
956
|
-
await this.cleanup();
|
|
957
|
-
return {
|
|
958
|
-
status: "error",
|
|
959
|
-
error: error instanceof Error ? error.message : String(error)
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
/**
|
|
964
|
-
* Start a browser-use session on the server
|
|
965
|
-
*/
|
|
966
|
-
async startSession(task) {
|
|
967
|
-
const response = await fetch(`${this.config.apiUrl}/api/browser-use/start`, {
|
|
968
|
-
method: "POST",
|
|
969
|
-
headers: { "Content-Type": "application/json" },
|
|
970
|
-
body: JSON.stringify({
|
|
971
|
-
product_id: this.config.productId,
|
|
972
|
-
task
|
|
973
|
-
})
|
|
974
|
-
});
|
|
975
|
-
if (!response.ok) {
|
|
976
|
-
const error = await response.json().catch(() => ({ detail: "Unknown error" }));
|
|
977
|
-
throw new Error(error.detail || `Failed to start session: ${response.status}`);
|
|
978
|
-
}
|
|
979
|
-
return response.json();
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* Process a step on the server
|
|
983
|
-
*/
|
|
984
|
-
async processStep(browserState, actionResult) {
|
|
985
|
-
const response = await fetch(`${this.config.apiUrl}/api/browser-use/step`, {
|
|
986
|
-
method: "POST",
|
|
987
|
-
headers: { "Content-Type": "application/json" },
|
|
988
|
-
body: JSON.stringify({
|
|
989
|
-
session_id: this.sessionId,
|
|
990
|
-
product_id: this.config.productId,
|
|
991
|
-
browser_state: browserState,
|
|
992
|
-
action_result: actionResult
|
|
993
|
-
})
|
|
994
|
-
});
|
|
995
|
-
if (!response.ok) {
|
|
996
|
-
const error = await response.json().catch(() => ({ detail: "Unknown error" }));
|
|
997
|
-
throw new Error(error.detail || `Failed to process step: ${response.status}`);
|
|
998
|
-
}
|
|
999
|
-
return response.json();
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Execute an action using PageController
|
|
1003
|
-
*/
|
|
1004
|
-
async executeAction(controller, action) {
|
|
1005
|
-
const actionName = Object.keys(action)[0];
|
|
1006
|
-
const actionParams = action[actionName];
|
|
1007
|
-
try {
|
|
1008
|
-
switch (actionName) {
|
|
1009
|
-
case "click_element_by_index": {
|
|
1010
|
-
const result = await controller.clickElement(actionParams.index);
|
|
1011
|
-
return result.message;
|
|
1012
|
-
}
|
|
1013
|
-
case "input_text": {
|
|
1014
|
-
const result = await controller.inputText(
|
|
1015
|
-
actionParams.index,
|
|
1016
|
-
actionParams.text
|
|
1017
|
-
);
|
|
1018
|
-
return result.message;
|
|
1019
|
-
}
|
|
1020
|
-
case "select_dropdown_option": {
|
|
1021
|
-
const result = await controller.selectOption(
|
|
1022
|
-
actionParams.index,
|
|
1023
|
-
actionParams.text
|
|
1024
|
-
);
|
|
1025
|
-
return result.message;
|
|
1026
|
-
}
|
|
1027
|
-
case "scroll": {
|
|
1028
|
-
const result = await controller.scroll({
|
|
1029
|
-
down: actionParams.down,
|
|
1030
|
-
numPages: actionParams.num_pages,
|
|
1031
|
-
pixels: actionParams.pixels,
|
|
1032
|
-
index: actionParams.index
|
|
1033
|
-
});
|
|
1034
|
-
return result.message;
|
|
1035
|
-
}
|
|
1036
|
-
case "scroll_horizontally": {
|
|
1037
|
-
const result = await controller.scrollHorizontally({
|
|
1038
|
-
right: actionParams.right,
|
|
1039
|
-
pixels: actionParams.pixels,
|
|
1040
|
-
index: actionParams.index
|
|
1041
|
-
});
|
|
1042
|
-
return result.message;
|
|
1043
|
-
}
|
|
1044
|
-
case "wait": {
|
|
1045
|
-
const seconds = actionParams.seconds || 1;
|
|
1046
|
-
await new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
|
|
1047
|
-
return `Waited ${seconds} seconds`;
|
|
1048
|
-
}
|
|
1049
|
-
case "done": {
|
|
1050
|
-
return "Task completed";
|
|
1051
|
-
}
|
|
1052
|
-
default:
|
|
1053
|
-
return `Unknown action: ${actionName}`;
|
|
1054
|
-
}
|
|
1055
|
-
} catch (error) {
|
|
1056
|
-
return `Action failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
/**
|
|
1060
|
-
* Cleanup resources
|
|
1061
|
-
*/
|
|
1062
|
-
async cleanup() {
|
|
1063
|
-
if (this.pageController) {
|
|
1064
|
-
try {
|
|
1065
|
-
await this.pageController.hideMask();
|
|
1066
|
-
await this.pageController.cleanUpHighlights();
|
|
1067
|
-
this.pageController.dispose();
|
|
1068
|
-
} catch (error) {
|
|
1069
|
-
console.warn("[CrowBrowserUse] Cleanup error:", error);
|
|
1070
|
-
}
|
|
1071
|
-
this.pageController = null;
|
|
1072
|
-
}
|
|
1073
|
-
if (this.sessionId) {
|
|
1074
|
-
try {
|
|
1075
|
-
await fetch(`${this.config.apiUrl}/api/browser-use/end`, {
|
|
1076
|
-
method: "POST",
|
|
1077
|
-
headers: { "Content-Type": "application/json" },
|
|
1078
|
-
body: JSON.stringify({
|
|
1079
|
-
session_id: this.sessionId,
|
|
1080
|
-
product_id: this.config.productId
|
|
1081
|
-
})
|
|
1082
|
-
});
|
|
1083
|
-
} catch (error) {
|
|
1084
|
-
}
|
|
1085
|
-
this.sessionId = null;
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
/**
|
|
1089
|
-
* Stop the current task
|
|
1090
|
-
*/
|
|
1091
|
-
async stop() {
|
|
1092
|
-
await this.cleanup();
|
|
1093
|
-
}
|
|
1094
|
-
};
|
|
1095
|
-
|
|
1096
|
-
exports.ConversationManager = ConversationManager;
|
|
1097
|
-
exports.CrowBrowserUse = CrowBrowserUse;
|
|
1098
|
-
exports.CrowClient = CrowClient;
|
|
1099
|
-
exports.DEFAULT_TOOLS = DEFAULT_TOOLS;
|
|
1100
|
-
exports.DEFAULT_TOOL_NAMES = DEFAULT_TOOL_NAMES;
|
|
1101
|
-
exports.IdentityManager = IdentityManager;
|
|
1102
|
-
exports.ToolManager = ToolManager;
|
|
1103
|
-
exports.parseSSEChunk = parseSSEChunk;
|
|
1104
|
-
exports.parseSSEData = parseSSEData;
|
|
1105
|
-
exports.streamResponse = streamResponse;
|
|
1106
|
-
//# sourceMappingURL=index.cjs.map
|
|
1107
|
-
//# sourceMappingURL=index.cjs.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("./browserUse-BOc9kyBK.cjs");class _{constructor(){this.state={token:null,metadata:{},isVerified:!1},this.listeners=new Set}identify(e){const{token:t,...s}=e;this.state={token:t,metadata:s,isVerified:!1},this.notify(),console.log("[Crow] User identified")}setVerified(e){this.state={...this.state,isVerified:e},this.notify()}reset(){this.state={token:null,metadata:{},isVerified:!1},this.notify(),console.log("[Crow] User reset")}getToken(){return this.state.token}getState(){return{...this.state}}isIdentified(){return this.state.token!==null}isVerified(){return this.state.isVerified}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){const e=this.getState();for(const t of this.listeners)t(e)}}class k{constructor(){this.handlers={}}register(e){for(const[t,s]of Object.entries(e))typeof s=="function"?(this.handlers[t]=s,console.log(`[Crow] Registered client tool: ${t}`)):console.warn(`[Crow] Skipping ${t}: handler is not a function`)}unregister(e){delete this.handlers[e],console.log(`[Crow] Unregistered client tool: ${e}`)}has(e){return e in this.handlers}getRegisteredTools(){return Object.keys(this.handlers)}async execute(e,t){const s=this.handlers[e];if(!s)return console.warn(`[Crow] No handler registered for tool: ${e}`),{status:"error",error:`No handler registered for tool: ${e}`};try{console.log(`[Crow] Executing client tool: ${e}`,t);const r=await s(t);return console.log(`[Crow] Tool ${e} completed:`,r),r}catch(r){const i=r instanceof Error?r.message:String(r);return console.error(`[Crow] Tool ${e} failed:`,r),{status:"error",error:i}}}}const T="crow_conv_";class C{constructor(e,t){this.currentId=null,this.productId=e,this.apiUrl=t,this.currentId=this.loadFromStorage()}getStorageKey(){return`${T}${this.productId}`}loadFromStorage(){try{return localStorage.getItem(this.getStorageKey())}catch{return null}}saveToStorage(e){try{localStorage.setItem(this.getStorageKey(),e)}catch{}}clearStorage(){try{localStorage.removeItem(this.getStorageKey())}catch{}}getCurrentId(){return this.currentId}setCurrentId(e){this.currentId=e,e?this.saveToStorage(e):this.clearStorage()}hasRestoredConversation(){return this.currentId!==null}clear(){this.currentId=null,this.clearStorage()}async getConversations(e){try{const t=await fetch(`${this.apiUrl}/api/chat/conversations?product_id=${this.productId}&identity_token=${encodeURIComponent(e)}`);if(!t.ok)throw new Error(`HTTP error: ${t.status}`);return(await t.json()).conversations||[]}catch(t){return console.error("[Crow] Failed to load conversations:",t),[]}}async loadHistory(e,t){try{const s=await fetch(`${this.apiUrl}/api/chat/conversations/${e}/history?product_id=${this.productId}&identity_token=${encodeURIComponent(t)}`);if(!s.ok)throw new Error(`HTTP error: ${s.status}`);const r=await s.json();return this.parseHistoryMessages(r.messages||[])}catch(s){return console.error("[Crow] Failed to load conversation history:",s),[]}}async loadAnonymousHistory(e){try{const t=await fetch(`${this.apiUrl}/api/chat/conversations/${e}/history/anonymous?product_id=${this.productId}`);if(!t.ok)throw new Error(`HTTP error: ${t.status}`);const s=await t.json();return this.parseHistoryMessages(s.messages||[])}catch(t){return console.error("[Crow] Failed to load anonymous conversation history:",t),[]}}parseHistoryMessages(e){return e.filter(t=>t.role!=="tool"&&!t.content.startsWith("[Client Tool Result:")).map((t,s)=>({id:`history-${s}`,content:this.parseContent(t.content),role:t.role==="assistant"?"assistant":"user",timestamp:new Date}))}parseContent(e){try{const t=JSON.parse(e);if(Array.isArray(t)){const s=t.find(r=>r.type==="text");return(s==null?void 0:s.text)||e}}catch{}if(e.includes("'type': 'text'")){const t=e.match(/\{'text':\s*'((?:[^'\\]|\\.)*)'\s*,\s*'type':\s*'text'/);if(t)return t[1].replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\'/g,"'")}return e}}function M(n){if(n==="[DONE]")return{type:"done"};try{const e=JSON.parse(n);switch(e.type){case"verification_status":return{type:"verification_status",isVerified:e.is_verified===!0};case"conversation_id":return{type:"conversation_id",conversationId:e.conversation_id};case"thinking":return e.status==="complete"?{type:"thinking_complete"}:null;case"thinking_token":return{type:"thinking",content:e.content||""};case"content":return{type:"content",text:e.content||"",accumulated:""};case"citations":return{type:"citations",citations:e.citations};case"error":return{type:"error",message:e.message||"Unknown error"};case"tool_call_start":return{type:"tool_call_start",toolName:e.tool_name,arguments:e.arguments||{}};case"tool_call_complete":return{type:"tool_call_complete",toolName:e.tool_name,success:e.success};case"client_tool_call":return{type:"client_tool_call",toolName:e.tool_name,arguments:e.arguments||{}};case"workflow_started":return{type:"workflow_started",name:e.name,todos:e.todos};case"todo_updated":return{type:"workflow_todo_updated",todoId:e.id,status:e.status};case"workflow_ended":return{type:"workflow_ended"};case"workflow_complete_prompt":return{type:"workflow_complete_prompt"};default:return null}}catch{return console.error("[Crow] Failed to parse SSE data:",n),null}}function*b(n){const e=n.split(`
|
|
3
|
+
`);for(const t of e)t.startsWith("data: ")&&(yield t.slice(6).trim())}async function*S(n,e){var i;const t=(i=n.body)==null?void 0:i.getReader();if(!t)throw new Error("Response body is not readable");const s=new TextDecoder;let r="";try{for(;;){if(e!=null&&e.aborted){t.cancel();return}const{done:l,value:d}=await t.read();if(l)break;const h=s.decode(d);for(const u of b(h)){const c=M(u);if(c&&(c.type==="content"?(r+=c.text,yield{...c,accumulated:r}):yield c,c.type==="done"))return}}}finally{t.releaseLock()}}const g={refreshPage:async()=>{try{return window.location.reload(),{status:"success",data:{message:"Page refresh initiated"}}}catch(n){return{status:"error",error:String(n)}}}},v=Object.keys(g),E="https://api.usecrow.org",L="claude-sonnet-4-20250514";class ${constructor(e){this.context={},this.abortController=null,this.callbacks={},this._messages=[],this.messageListeners=new Set,this._isLoading=!1,this.loadingListeners=new Set,this.config={productId:e.productId,apiUrl:e.apiUrl||E,model:e.model||L},this.identity=new _,this.tools=new k,this.conversations=new C(this.config.productId,this.config.apiUrl),this.tools.register(g),console.log("[Crow] Default tools registered:",v.join(", ")),this.identity.subscribe(t=>{var s,r;(r=(s=this.callbacks).onVerificationStatus)==null||r.call(s,t.isVerified)})}get productId(){return this.config.productId}get apiUrl(){return this.config.apiUrl}get model(){return this.config.model}set model(e){this.config.model=e}on(e){this.callbacks={...this.callbacks,...e}}identify(e){this.identity.identify(e)}resetUser(){this.identity.reset(),this.clearMessages()}isIdentified(){return this.identity.isIdentified()}isVerified(){return this.identity.isVerified()}registerTools(e){this.tools.register(e)}unregisterTool(e){this.tools.unregister(e)}getRegisteredTools(){return this.tools.getRegisteredTools()}setContext(e){this.context={...this.context,...e}}clearContext(){this.context={}}get messages(){return[...this._messages]}get isLoading(){return this._isLoading}onMessages(e){return this.messageListeners.add(e),()=>this.messageListeners.delete(e)}onLoading(e){return this.loadingListeners.add(e),()=>this.loadingListeners.delete(e)}clearMessages(){this._messages=[],this.conversations.clear(),this.notifyMessages()}loadMessages(e){this._messages=e,this.notifyMessages()}notifyMessages(){const e=this.messages;for(const t of this.messageListeners)t(e)}setLoading(e){this._isLoading=e;for(const t of this.loadingListeners)t(e)}addMessage(e){var t,s;this._messages=[...this._messages,e],this.notifyMessages(),(s=(t=this.callbacks).onMessage)==null||s.call(t,e)}updateMessage(e,t){var s,r;this._messages=this._messages.map(i=>i.id===e?{...i,...t}:i),this.notifyMessages(),(r=(s=this.callbacks).onMessageUpdate)==null||r.call(s,e,t)}generateMessageId(e){return`${e}-${Date.now()}-${Math.random().toString(36).slice(2,9)}`}get conversationId(){return this.conversations.getCurrentId()}set conversationId(e){this.conversations.setCurrentId(e)}async getConversations(){const e=this.identity.getToken();return e?this.conversations.getConversations(e):(console.warn("[Crow] Cannot get conversations: user not identified"),[])}async loadHistory(e){const t=this.identity.getToken();return t?this.conversations.loadHistory(e,t):this.conversations.loadAnonymousHistory(e)}async switchConversation(e){const t=await this.loadHistory(e);this.conversations.setCurrentId(e),this.loadMessages(t)}async*sendMessage(e){var l,d,h,u,c,f,p,y,m,w;if(!e.trim())return;const t=this.generateMessageId("user");this.addMessage({id:t,content:e,role:"user",timestamp:new Date});const s=this.generateMessageId("assistant");this.addMessage({id:s,content:"",role:"assistant",timestamp:new Date}),this.setLoading(!0),this.abortController=new AbortController;let r="",i="";try{const a=await fetch(`${this.config.apiUrl}/api/chat/message`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({product_id:this.config.productId,message:e,conversation_id:this.conversations.getCurrentId(),identity_token:this.identity.getToken(),model:this.config.model,context:Object.keys(this.context).length>0?this.context:void 0}),signal:this.abortController.signal});if(!a.ok)throw new Error(`HTTP error: ${a.status}`);for await(const o of S(a,this.abortController.signal)){switch(o.type){case"content":r=o.accumulated,this.updateMessage(s,{content:r});break;case"thinking":i+=o.content,this.updateMessage(s,{thinking:i});break;case"thinking_complete":this.updateMessage(s,{thinkingComplete:!0});break;case"citations":this.updateMessage(s,{citations:o.citations});break;case"verification_status":this.identity.setVerified(o.isVerified);break;case"conversation_id":this.conversations.setCurrentId(o.conversationId);break;case"client_tool_call":await this.tools.execute(o.toolName,o.arguments),(d=(l=this.callbacks).onToolCall)==null||d.call(l,o);break;case"tool_call_start":case"tool_call_complete":(u=(h=this.callbacks).onToolCall)==null||u.call(h,o);break;case"workflow_started":case"workflow_todo_updated":case"workflow_ended":case"workflow_complete_prompt":(f=(c=this.callbacks).onWorkflow)==null||f.call(c,o);break;case"error":this.updateMessage(s,{content:o.message}),(y=(p=this.callbacks).onError)==null||y.call(p,new Error(o.message));break}yield o}}catch(a){if(a instanceof Error&&a.name==="AbortError"){r?this.updateMessage(s,{content:r}):(this._messages=this._messages.filter(o=>o.id!==s),this.notifyMessages());return}console.error("[Crow] Error:",a),this.updateMessage(s,{content:"Sorry, I encountered an error. Please try again."}),(w=(m=this.callbacks).onError)==null||w.call(m,a instanceof Error?a:new Error(String(a)))}finally{this.setLoading(!1),this.abortController=null}}async send(e){let t=null;for await(const r of this.sendMessage(e))if(r.type==="done")break;const s=this.messages;return s.length>0&&(t=s[s.length-1],t.role==="assistant")?t:null}stop(){this.abortController&&(this.abortController.abort(),this.setLoading(!1))}destroy(){this.stop(),this.messageListeners.clear(),this.loadingListeners.clear()}}exports.CrowBrowserUse=I.CrowBrowserUse;exports.ConversationManager=C;exports.CrowClient=$;exports.DEFAULT_TOOLS=g;exports.DEFAULT_TOOL_NAMES=v;exports.IdentityManager=_;exports.ToolManager=k;exports.parseSSEChunk=b;exports.parseSSEData=M;exports.streamResponse=S;
|