@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/browser.d.ts
CHANGED
|
@@ -1,52 +1,342 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* //
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
1
|
+
declare interface ActionResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Structured browser state for LLM consumption
|
|
8
|
+
*/
|
|
9
|
+
declare interface BrowserState {
|
|
10
|
+
url: string;
|
|
11
|
+
title: string;
|
|
12
|
+
/** Page info + scroll position hint (e.g. "Page info: 1920x1080px...\n[Start of page]") */
|
|
13
|
+
header: string;
|
|
14
|
+
/** Simplified HTML of interactive elements */
|
|
15
|
+
content: string;
|
|
16
|
+
/** Page footer hint (e.g. "... 300 pixels below ..." or "[End of page]") */
|
|
17
|
+
footer: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
declare interface BrowserUseConfig {
|
|
21
|
+
productId: string;
|
|
22
|
+
apiUrl: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export declare interface BrowserUseToolArgs {
|
|
26
|
+
instruction: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export declare interface BrowserUseToolConfig {
|
|
30
|
+
productId: string;
|
|
31
|
+
apiUrl: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
declare function cleanUpHighlights(): void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create a browser_use tool handler for registration with CrowClient or widget.
|
|
38
|
+
*
|
|
39
|
+
* @param config - Configuration with productId and apiUrl
|
|
40
|
+
* @returns Async function that executes browser automation tasks
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Widget usage
|
|
45
|
+
* window.__crow_client_tools.browser_use = createBrowserUseTool({ productId, apiUrl });
|
|
46
|
+
*
|
|
47
|
+
* // SDK usage
|
|
48
|
+
* client.registerTools({
|
|
49
|
+
* browser_use: createBrowserUseTool({ productId, apiUrl })
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function createBrowserUseTool(config: BrowserUseToolConfig): (args: BrowserUseToolArgs | Record<string, unknown>) => Promise<ToolResult>;
|
|
54
|
+
|
|
55
|
+
export declare class CrowBrowserUse {
|
|
56
|
+
private config;
|
|
57
|
+
private pageController;
|
|
58
|
+
private sessionId;
|
|
59
|
+
private maxSteps;
|
|
60
|
+
constructor(config: BrowserUseConfig);
|
|
61
|
+
/**
|
|
62
|
+
* Initialize PageController with non-blocking pointer
|
|
63
|
+
*/
|
|
64
|
+
private initPageController;
|
|
65
|
+
/**
|
|
66
|
+
* Execute a browser automation task
|
|
67
|
+
*/
|
|
68
|
+
execute(task: string): Promise<ToolResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Start a browser-use session on the server
|
|
71
|
+
*/
|
|
72
|
+
private startSession;
|
|
73
|
+
/**
|
|
74
|
+
* Process a step on the server
|
|
75
|
+
*/
|
|
76
|
+
private processStep;
|
|
77
|
+
/**
|
|
78
|
+
* Execute an action using PageController
|
|
79
|
+
*/
|
|
80
|
+
private executeAction;
|
|
81
|
+
/**
|
|
82
|
+
* Cleanup resources
|
|
83
|
+
*/
|
|
84
|
+
private cleanup;
|
|
85
|
+
/**
|
|
86
|
+
* Stop the current task
|
|
87
|
+
*/
|
|
88
|
+
stop(): Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare namespace dom {
|
|
92
|
+
export {
|
|
93
|
+
getFlatTree,
|
|
94
|
+
flatTreeToString,
|
|
95
|
+
getSelectorMap,
|
|
96
|
+
getElementTextMap,
|
|
97
|
+
cleanUpHighlights,
|
|
98
|
+
DomConfig,
|
|
99
|
+
getAllTextTillNextClickableElement
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
declare interface DomConfig {
|
|
104
|
+
interactiveBlacklist?: (Element | (() => Element))[];
|
|
105
|
+
interactiveWhitelist?: (Element | (() => Element))[];
|
|
106
|
+
include_attributes?: string[];
|
|
107
|
+
highlightOpacity?: number;
|
|
108
|
+
highlightLabelOpacity?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
declare type DomNode = TextDomNode | ElementDomNode | InteractiveElementDomNode;
|
|
112
|
+
|
|
113
|
+
declare interface ElementDomNode {
|
|
114
|
+
tagName: string;
|
|
115
|
+
attributes?: Record<string, string>;
|
|
116
|
+
xpath?: string;
|
|
117
|
+
children?: string[];
|
|
118
|
+
isVisible?: boolean;
|
|
119
|
+
isTopElement?: boolean;
|
|
120
|
+
isInViewport?: boolean;
|
|
121
|
+
isNew?: boolean;
|
|
122
|
+
isInteractive?: false;
|
|
123
|
+
highlightIndex?: number;
|
|
124
|
+
extra?: Record<string, any>;
|
|
125
|
+
[key: string]: unknown;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Derived from @page-agent/page-controller
|
|
130
|
+
* Original: https://github.com/alibaba/page-agent
|
|
131
|
+
* Copyright (c) 2025 Alibaba Group Holding Limited
|
|
132
|
+
* Licensed under MIT License
|
|
133
|
+
*/
|
|
134
|
+
declare interface FlatDomTree {
|
|
135
|
+
rootId: string;
|
|
136
|
+
map: Record<string, DomNode>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 对应 python 中的 views::clickable_elements_to_string,
|
|
141
|
+
* 将 dom 信息处理成适合 llm 阅读的文本格式
|
|
142
|
+
* @形如
|
|
143
|
+
* ``` text
|
|
144
|
+
* [0]<a aria-label=page-agent.js 首页 />
|
|
145
|
+
* [1]<div >P />
|
|
146
|
+
* [2]<div >page-agent.js
|
|
147
|
+
* UI Agent in your webpage />
|
|
148
|
+
* [3]<a >文档 />
|
|
149
|
+
* [4]<a aria-label=查看源码(在新窗口打开)>源码 />
|
|
150
|
+
* UI Agent in your webpage
|
|
151
|
+
* 用户输入需求,AI 理解页面并自动操作。
|
|
152
|
+
* [5]<a role=button>快速开始 />
|
|
153
|
+
* [6]<a role=button>查看文档 />
|
|
154
|
+
* 无需后端
|
|
155
|
+
* ```
|
|
156
|
+
* 其中可交互元素用序号标出,提示llm可以用序号操作。
|
|
157
|
+
* 缩进代表父子关系。
|
|
158
|
+
* 普通文本则直接列出来。
|
|
159
|
+
*
|
|
160
|
+
* @todo 数据脱敏过滤器
|
|
161
|
+
*/
|
|
162
|
+
declare function flatTreeToString(flatTree: FlatDomTree, include_attributes?: string[]): string;
|
|
163
|
+
|
|
164
|
+
declare const getAllTextTillNextClickableElement: (node: TreeNode, maxDepth?: number) => string;
|
|
165
|
+
|
|
166
|
+
declare function getElementTextMap(simplifiedHTML: string): Map<number, string>;
|
|
167
|
+
|
|
168
|
+
declare function getFlatTree(config: DomConfig): FlatDomTree;
|
|
169
|
+
|
|
170
|
+
declare function getSelectorMap(flatTree: FlatDomTree): Map<number, InteractiveElementDomNode>;
|
|
171
|
+
|
|
172
|
+
declare interface InteractiveElementDomNode {
|
|
173
|
+
tagName: string;
|
|
174
|
+
attributes?: Record<string, string>;
|
|
175
|
+
xpath?: string;
|
|
176
|
+
children?: string[];
|
|
177
|
+
isVisible?: boolean;
|
|
178
|
+
isTopElement?: boolean;
|
|
179
|
+
isInViewport?: boolean;
|
|
180
|
+
isInteractive: true;
|
|
181
|
+
highlightIndex: number;
|
|
182
|
+
/**
|
|
183
|
+
* 可交互元素的 dom 引用
|
|
184
|
+
*/
|
|
185
|
+
ref: HTMLElement;
|
|
186
|
+
[key: string]: unknown;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* PageController manages DOM state and element interactions.
|
|
191
|
+
* It provides async methods for all DOM operations, keeping state isolated.
|
|
192
|
+
*
|
|
193
|
+
* @lifecycle
|
|
194
|
+
* - beforeUpdate: Emitted before the DOM tree is updated.
|
|
195
|
+
* - afterUpdate: Emitted after the DOM tree is updated.
|
|
196
|
+
*/
|
|
197
|
+
export declare class PageController extends EventTarget {
|
|
198
|
+
private config;
|
|
199
|
+
/** Corresponds to eval_page in browser-use */
|
|
200
|
+
private flatTree;
|
|
201
|
+
/**
|
|
202
|
+
* All highlighted index-mapped interactive elements
|
|
203
|
+
* Corresponds to DOMState.selector_map in browser-use
|
|
204
|
+
*/
|
|
205
|
+
private selectorMap;
|
|
206
|
+
/** Index -> element text description mapping */
|
|
207
|
+
private elementTextMap;
|
|
208
|
+
/**
|
|
209
|
+
* Simplified HTML for LLM consumption.
|
|
210
|
+
* Corresponds to clickable_elements_to_string in browser-use
|
|
211
|
+
*/
|
|
212
|
+
private simplifiedHTML;
|
|
213
|
+
/** last time the tree was updated */
|
|
214
|
+
private lastTimeUpdate;
|
|
215
|
+
/** Whether the tree has been indexed at least once */
|
|
216
|
+
private isIndexed;
|
|
217
|
+
/** Visual mask overlay for blocking user interaction during automation */
|
|
218
|
+
private mask;
|
|
219
|
+
private maskReady;
|
|
220
|
+
constructor(config?: PageControllerConfig);
|
|
221
|
+
/**
|
|
222
|
+
* Initialize mask asynchronously (dynamic import to avoid CSS loading in Node)
|
|
223
|
+
*/
|
|
224
|
+
initMask(): void;
|
|
225
|
+
/**
|
|
226
|
+
* Get current page URL
|
|
227
|
+
*/
|
|
228
|
+
getCurrentUrl(): Promise<string>;
|
|
229
|
+
/**
|
|
230
|
+
* Get last tree update timestamp
|
|
231
|
+
*/
|
|
232
|
+
getLastUpdateTime(): Promise<number>;
|
|
233
|
+
/**
|
|
234
|
+
* Get structured browser state for LLM consumption.
|
|
235
|
+
* Automatically calls updateTree() to refresh the DOM state.
|
|
236
|
+
*/
|
|
237
|
+
getBrowserState(): Promise<BrowserState>;
|
|
238
|
+
/**
|
|
239
|
+
* Update DOM tree, returns simplified HTML for LLM.
|
|
240
|
+
* This is the main method to refresh the page state.
|
|
241
|
+
* Automatically bypasses mask during DOM extraction if enabled.
|
|
242
|
+
*/
|
|
243
|
+
updateTree(): Promise<string>;
|
|
244
|
+
/**
|
|
245
|
+
* Clean up all element highlights
|
|
246
|
+
*/
|
|
247
|
+
cleanUpHighlights(): Promise<void>;
|
|
248
|
+
/**
|
|
249
|
+
* Ensure the tree has been indexed before any index-based operation.
|
|
250
|
+
* Throws if updateTree() hasn't been called yet.
|
|
251
|
+
*/
|
|
252
|
+
private assertIndexed;
|
|
253
|
+
/**
|
|
254
|
+
* Click element by index
|
|
255
|
+
*/
|
|
256
|
+
clickElement(index: number): Promise<ActionResult>;
|
|
257
|
+
/**
|
|
258
|
+
* Input text into element by index
|
|
259
|
+
*/
|
|
260
|
+
inputText(index: number, text: string): Promise<ActionResult>;
|
|
261
|
+
/**
|
|
262
|
+
* Select dropdown option by index and option text
|
|
263
|
+
*/
|
|
264
|
+
selectOption(index: number, optionText: string): Promise<ActionResult>;
|
|
265
|
+
/**
|
|
266
|
+
* Scroll vertically
|
|
267
|
+
*/
|
|
268
|
+
scroll(options: {
|
|
269
|
+
down: boolean;
|
|
270
|
+
numPages: number;
|
|
271
|
+
pixels?: number;
|
|
272
|
+
index?: number;
|
|
273
|
+
}): Promise<ActionResult>;
|
|
274
|
+
/**
|
|
275
|
+
* Scroll horizontally
|
|
276
|
+
*/
|
|
277
|
+
scrollHorizontally(options: {
|
|
278
|
+
right: boolean;
|
|
279
|
+
pixels: number;
|
|
280
|
+
index?: number;
|
|
281
|
+
}): Promise<ActionResult>;
|
|
282
|
+
/**
|
|
283
|
+
* Execute arbitrary JavaScript on the page
|
|
284
|
+
*/
|
|
285
|
+
executeJavascript(script: string): Promise<ActionResult>;
|
|
286
|
+
/**
|
|
287
|
+
* Show the visual mask overlay.
|
|
288
|
+
* Only works after mask is setup.
|
|
289
|
+
*/
|
|
290
|
+
showMask(): Promise<void>;
|
|
291
|
+
/**
|
|
292
|
+
* Hide the visual mask overlay.
|
|
293
|
+
* Only works after mask is setup.
|
|
294
|
+
*/
|
|
295
|
+
hideMask(): Promise<void>;
|
|
296
|
+
/**
|
|
297
|
+
* Dispose and clean up resources
|
|
298
|
+
*/
|
|
299
|
+
dispose(): void;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Configuration for PageController
|
|
304
|
+
*/
|
|
305
|
+
declare interface PageControllerConfig extends dom.DomConfig {
|
|
306
|
+
viewportExpansion?: number;
|
|
307
|
+
/** Enable visual mask overlay during operations (default: false) */
|
|
308
|
+
enableMask?: boolean;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
declare interface TextDomNode {
|
|
312
|
+
type: 'TEXT_NODE';
|
|
313
|
+
text: string;
|
|
314
|
+
isVisible: boolean;
|
|
315
|
+
[key: string]: unknown;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
declare interface ToolResult {
|
|
319
|
+
status: 'success' | 'error';
|
|
320
|
+
data?: unknown;
|
|
321
|
+
error?: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* elementsToString 内部使用的类型
|
|
326
|
+
*/
|
|
327
|
+
declare interface TreeNode {
|
|
328
|
+
type: 'text' | 'element';
|
|
329
|
+
parent: TreeNode | null;
|
|
330
|
+
children: TreeNode[];
|
|
331
|
+
isVisible: boolean;
|
|
332
|
+
text?: string;
|
|
333
|
+
tagName?: string;
|
|
334
|
+
attributes?: Record<string, string>;
|
|
335
|
+
isInteractive?: boolean;
|
|
336
|
+
isTopElement?: boolean;
|
|
337
|
+
isNew?: boolean;
|
|
338
|
+
highlightIndex?: number;
|
|
339
|
+
extra?: Record<string, any>;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export { }
|
package/dist/browser.js
CHANGED
|
@@ -1,278 +1,20 @@
|
|
|
1
|
-
import { PageController } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return injectedPageController;
|
|
15
|
-
}
|
|
16
|
-
if (!PageControllerModule) {
|
|
17
|
-
try {
|
|
18
|
-
PageControllerModule = await import('@page-agent/page-controller');
|
|
19
|
-
} catch (error) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
'PageController not available. Either import from "@usecrow/client/browser" or install @page-agent/page-controller as a dependency.'
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return PageControllerModule.PageController;
|
|
26
|
-
}
|
|
27
|
-
var CrowBrowserUse = class {
|
|
28
|
-
constructor(config) {
|
|
29
|
-
this.pageController = null;
|
|
30
|
-
this.sessionId = null;
|
|
31
|
-
this.maxSteps = 20;
|
|
32
|
-
this.config = config;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Initialize PageController with non-blocking pointer
|
|
36
|
-
*/
|
|
37
|
-
async initPageController() {
|
|
38
|
-
if (this.pageController) {
|
|
39
|
-
return this.pageController;
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
const PageController2 = await getPageController();
|
|
43
|
-
this.pageController = new PageController2({
|
|
44
|
-
enableMask: true,
|
|
45
|
-
viewportExpansion: 500,
|
|
46
|
-
highlightLabelOpacity: 0,
|
|
47
|
-
// Hide numbered labels from users
|
|
48
|
-
highlightOpacity: 0
|
|
49
|
-
// Hide highlight boxes from users
|
|
50
|
-
});
|
|
51
|
-
await this.pageController.showMask();
|
|
52
|
-
const mask = this.pageController.mask;
|
|
53
|
-
if (mask?.wrapper) {
|
|
54
|
-
mask.wrapper.style.pointerEvents = "none";
|
|
55
|
-
}
|
|
56
|
-
console.log("[CrowBrowserUse] PageController initialized with non-blocking pointer");
|
|
57
|
-
return this.pageController;
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.error("[CrowBrowserUse] Failed to import @page-agent/page-controller:", error);
|
|
60
|
-
throw new Error(
|
|
61
|
-
"Failed to initialize browser automation. Make sure @page-agent/page-controller is installed."
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Execute a browser automation task
|
|
67
|
-
*/
|
|
68
|
-
async execute(task) {
|
|
69
|
-
console.log("[CrowBrowserUse] Starting task:", task);
|
|
70
|
-
try {
|
|
71
|
-
const controller = await this.initPageController();
|
|
72
|
-
const startResponse = await this.startSession(task);
|
|
73
|
-
this.sessionId = startResponse.session_id;
|
|
74
|
-
this.maxSteps = startResponse.max_steps;
|
|
75
|
-
console.log("[CrowBrowserUse] Session started:", this.sessionId);
|
|
76
|
-
let stepCount = 0;
|
|
77
|
-
let lastActionResult;
|
|
78
|
-
while (stepCount < this.maxSteps) {
|
|
79
|
-
stepCount++;
|
|
80
|
-
const browserState = await controller.getBrowserState();
|
|
81
|
-
const stepResponse = await this.processStep(browserState, lastActionResult);
|
|
82
|
-
if (stepResponse.done) {
|
|
83
|
-
console.log("[CrowBrowserUse] Task completed:", stepResponse.message);
|
|
84
|
-
await this.cleanup();
|
|
85
|
-
return {
|
|
86
|
-
status: stepResponse.success ? "success" : "error",
|
|
87
|
-
data: {
|
|
88
|
-
message: stepResponse.message,
|
|
89
|
-
steps: stepCount
|
|
90
|
-
},
|
|
91
|
-
error: stepResponse.success ? void 0 : stepResponse.message
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
if (stepResponse.error) {
|
|
95
|
-
console.error("[CrowBrowserUse] Error:", stepResponse.error);
|
|
96
|
-
await this.cleanup();
|
|
97
|
-
return {
|
|
98
|
-
status: "error",
|
|
99
|
-
error: stepResponse.error
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
if (stepResponse.action) {
|
|
103
|
-
lastActionResult = await this.executeAction(controller, stepResponse.action);
|
|
104
|
-
console.log(`[CrowBrowserUse] Step ${stepCount}:`, lastActionResult);
|
|
105
|
-
}
|
|
106
|
-
if (stepResponse.reflection) {
|
|
107
|
-
console.log("[CrowBrowserUse] Reflection:", stepResponse.reflection.next_goal);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
await this.cleanup();
|
|
111
|
-
return {
|
|
112
|
-
status: "error",
|
|
113
|
-
error: `Task incomplete after ${this.maxSteps} steps`
|
|
114
|
-
};
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error("[CrowBrowserUse] Error:", error);
|
|
117
|
-
await this.cleanup();
|
|
118
|
-
return {
|
|
119
|
-
status: "error",
|
|
120
|
-
error: error instanceof Error ? error.message : String(error)
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Start a browser-use session on the server
|
|
126
|
-
*/
|
|
127
|
-
async startSession(task) {
|
|
128
|
-
const response = await fetch(`${this.config.apiUrl}/api/browser-use/start`, {
|
|
129
|
-
method: "POST",
|
|
130
|
-
headers: { "Content-Type": "application/json" },
|
|
131
|
-
body: JSON.stringify({
|
|
132
|
-
product_id: this.config.productId,
|
|
133
|
-
task
|
|
134
|
-
})
|
|
135
|
-
});
|
|
136
|
-
if (!response.ok) {
|
|
137
|
-
const error = await response.json().catch(() => ({ detail: "Unknown error" }));
|
|
138
|
-
throw new Error(error.detail || `Failed to start session: ${response.status}`);
|
|
139
|
-
}
|
|
140
|
-
return response.json();
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Process a step on the server
|
|
144
|
-
*/
|
|
145
|
-
async processStep(browserState, actionResult) {
|
|
146
|
-
const response = await fetch(`${this.config.apiUrl}/api/browser-use/step`, {
|
|
147
|
-
method: "POST",
|
|
148
|
-
headers: { "Content-Type": "application/json" },
|
|
149
|
-
body: JSON.stringify({
|
|
150
|
-
session_id: this.sessionId,
|
|
151
|
-
product_id: this.config.productId,
|
|
152
|
-
browser_state: browserState,
|
|
153
|
-
action_result: actionResult
|
|
154
|
-
})
|
|
155
|
-
});
|
|
156
|
-
if (!response.ok) {
|
|
157
|
-
const error = await response.json().catch(() => ({ detail: "Unknown error" }));
|
|
158
|
-
throw new Error(error.detail || `Failed to process step: ${response.status}`);
|
|
159
|
-
}
|
|
160
|
-
return response.json();
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Execute an action using PageController
|
|
164
|
-
*/
|
|
165
|
-
async executeAction(controller, action) {
|
|
166
|
-
const actionName = Object.keys(action)[0];
|
|
167
|
-
const actionParams = action[actionName];
|
|
168
|
-
try {
|
|
169
|
-
switch (actionName) {
|
|
170
|
-
case "click_element_by_index": {
|
|
171
|
-
const result = await controller.clickElement(actionParams.index);
|
|
172
|
-
return result.message;
|
|
173
|
-
}
|
|
174
|
-
case "input_text": {
|
|
175
|
-
const result = await controller.inputText(
|
|
176
|
-
actionParams.index,
|
|
177
|
-
actionParams.text
|
|
178
|
-
);
|
|
179
|
-
return result.message;
|
|
180
|
-
}
|
|
181
|
-
case "select_dropdown_option": {
|
|
182
|
-
const result = await controller.selectOption(
|
|
183
|
-
actionParams.index,
|
|
184
|
-
actionParams.text
|
|
185
|
-
);
|
|
186
|
-
return result.message;
|
|
187
|
-
}
|
|
188
|
-
case "scroll": {
|
|
189
|
-
const result = await controller.scroll({
|
|
190
|
-
down: actionParams.down,
|
|
191
|
-
numPages: actionParams.num_pages,
|
|
192
|
-
pixels: actionParams.pixels,
|
|
193
|
-
index: actionParams.index
|
|
194
|
-
});
|
|
195
|
-
return result.message;
|
|
196
|
-
}
|
|
197
|
-
case "scroll_horizontally": {
|
|
198
|
-
const result = await controller.scrollHorizontally({
|
|
199
|
-
right: actionParams.right,
|
|
200
|
-
pixels: actionParams.pixels,
|
|
201
|
-
index: actionParams.index
|
|
202
|
-
});
|
|
203
|
-
return result.message;
|
|
204
|
-
}
|
|
205
|
-
case "wait": {
|
|
206
|
-
const seconds = actionParams.seconds || 1;
|
|
207
|
-
await new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
|
|
208
|
-
return `Waited ${seconds} seconds`;
|
|
209
|
-
}
|
|
210
|
-
case "done": {
|
|
211
|
-
return "Task completed";
|
|
212
|
-
}
|
|
213
|
-
default:
|
|
214
|
-
return `Unknown action: ${actionName}`;
|
|
215
|
-
}
|
|
216
|
-
} catch (error) {
|
|
217
|
-
return `Action failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Cleanup resources
|
|
222
|
-
*/
|
|
223
|
-
async cleanup() {
|
|
224
|
-
if (this.pageController) {
|
|
225
|
-
try {
|
|
226
|
-
await this.pageController.hideMask();
|
|
227
|
-
await this.pageController.cleanUpHighlights();
|
|
228
|
-
this.pageController.dispose();
|
|
229
|
-
} catch (error) {
|
|
230
|
-
console.warn("[CrowBrowserUse] Cleanup error:", error);
|
|
231
|
-
}
|
|
232
|
-
this.pageController = null;
|
|
233
|
-
}
|
|
234
|
-
if (this.sessionId) {
|
|
235
|
-
try {
|
|
236
|
-
await fetch(`${this.config.apiUrl}/api/browser-use/end`, {
|
|
237
|
-
method: "POST",
|
|
238
|
-
headers: { "Content-Type": "application/json" },
|
|
239
|
-
body: JSON.stringify({
|
|
240
|
-
session_id: this.sessionId,
|
|
241
|
-
product_id: this.config.productId
|
|
242
|
-
})
|
|
243
|
-
});
|
|
244
|
-
} catch (error) {
|
|
245
|
-
}
|
|
246
|
-
this.sessionId = null;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Stop the current task
|
|
251
|
-
*/
|
|
252
|
-
async stop() {
|
|
253
|
-
await this.cleanup();
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
// src/browser.ts
|
|
258
|
-
setPageController(PageController);
|
|
259
|
-
function createBrowserUseTool(config) {
|
|
260
|
-
return async (args) => {
|
|
261
|
-
const instruction = args.instruction || args.instruction;
|
|
262
|
-
if (!instruction) {
|
|
263
|
-
return {
|
|
264
|
-
status: "error",
|
|
265
|
-
error: "Missing instruction parameter for browser_use tool"
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
const browserUse = new CrowBrowserUse({
|
|
269
|
-
productId: config.productId,
|
|
270
|
-
apiUrl: config.apiUrl
|
|
271
|
-
});
|
|
272
|
-
return browserUse.execute(instruction);
|
|
1
|
+
import { PageController as t } from "./PageController-KoeqDxMP.js";
|
|
2
|
+
import { C as s, s as n } from "./browserUse-BbPG4pH1.js";
|
|
3
|
+
n(t);
|
|
4
|
+
function a(r) {
|
|
5
|
+
return async (o) => {
|
|
6
|
+
const e = o.instruction || o.instruction;
|
|
7
|
+
return e ? new s({
|
|
8
|
+
productId: r.productId,
|
|
9
|
+
apiUrl: r.apiUrl
|
|
10
|
+
}).execute(e) : {
|
|
11
|
+
status: "error",
|
|
12
|
+
error: "Missing instruction parameter for browser_use tool"
|
|
13
|
+
};
|
|
273
14
|
};
|
|
274
15
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
16
|
+
export {
|
|
17
|
+
s as CrowBrowserUse,
|
|
18
|
+
t as PageController,
|
|
19
|
+
a as createBrowserUseTool
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";let c=null,l=null;function p(i){c=i}async function w(){if(c)return c;if(!l)try{l=await Promise.resolve().then(()=>require("./PageController-GcMFZYwU.cjs"))}catch{throw new Error('PageController not available. Either import from "@usecrow/client/browser" or use the bundled version.')}return l.PageController}class u{constructor(t){this.pageController=null,this.sessionId=null,this.maxSteps=20,this.config=t}async initPageController(){if(this.pageController)return this.pageController;try{const t=await w();this.pageController=new t({enableMask:!0,viewportExpansion:500,highlightLabelOpacity:0,highlightOpacity:0}),await this.pageController.showMask();const e=this.pageController.mask;return e!=null&&e.wrapper&&(e.wrapper.style.pointerEvents="none"),console.log("[CrowBrowserUse] PageController initialized with non-blocking pointer"),this.pageController}catch(t){throw console.error("[CrowBrowserUse] Failed to initialize PageController:",t),new Error("Failed to initialize browser automation. Please import from @usecrow/client/browser.")}}async execute(t){console.log("[CrowBrowserUse] Starting task:",t);try{const e=await this.initPageController(),o=await this.startSession(t);this.sessionId=o.session_id,this.maxSteps=o.max_steps,console.log("[CrowBrowserUse] Session started:",this.sessionId);let r=0,s;for(;r<this.maxSteps;){r++;const a=await e.getBrowserState(),n=await this.processStep(a,s);if(n.done)return console.log("[CrowBrowserUse] Task completed:",n.message),await this.cleanup(),{status:n.success?"success":"error",data:{message:n.message,steps:r},error:n.success?void 0:n.message};if(n.error)return console.error("[CrowBrowserUse] Error:",n.error),await this.cleanup(),{status:"error",error:n.error};n.action&&(s=await this.executeAction(e,n.action),console.log(`[CrowBrowserUse] Step ${r}:`,s)),n.reflection&&console.log("[CrowBrowserUse] Reflection:",n.reflection.next_goal)}return await this.cleanup(),{status:"error",error:`Task incomplete after ${this.maxSteps} steps`}}catch(e){return console.error("[CrowBrowserUse] Error:",e),await this.cleanup(),{status:"error",error:e instanceof Error?e.message:String(e)}}}async startSession(t){const e=await fetch(`${this.config.apiUrl}/api/browser-use/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({product_id:this.config.productId,task:t})});if(!e.ok){const o=await e.json().catch(()=>({detail:"Unknown error"}));throw new Error(o.detail||`Failed to start session: ${e.status}`)}return e.json()}async processStep(t,e){const o=await fetch(`${this.config.apiUrl}/api/browser-use/step`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({session_id:this.sessionId,product_id:this.config.productId,browser_state:t,action_result:e})});if(!o.ok){const r=await o.json().catch(()=>({detail:"Unknown error"}));throw new Error(r.detail||`Failed to process step: ${o.status}`)}return o.json()}async executeAction(t,e){const o=Object.keys(e)[0],r=e[o];try{switch(o){case"click_element_by_index":return(await t.clickElement(r.index)).message;case"input_text":return(await t.inputText(r.index,r.text)).message;case"select_dropdown_option":return(await t.selectOption(r.index,r.text)).message;case"scroll":return(await t.scroll({down:r.down,numPages:r.num_pages,pixels:r.pixels,index:r.index})).message;case"scroll_horizontally":return(await t.scrollHorizontally({right:r.right,pixels:r.pixels,index:r.index})).message;case"wait":{const s=r.seconds||1;return await new Promise(a=>setTimeout(a,s*1e3)),`Waited ${s} seconds`}case"done":return"Task completed";default:return`Unknown action: ${o}`}}catch(s){return`Action failed: ${s instanceof Error?s.message:String(s)}`}}async cleanup(){if(this.pageController){try{await this.pageController.hideMask(),await this.pageController.cleanUpHighlights(),this.pageController.dispose()}catch(t){console.warn("[CrowBrowserUse] Cleanup error:",t)}this.pageController=null}if(this.sessionId){try{await fetch(`${this.config.apiUrl}/api/browser-use/end`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({session_id:this.sessionId,product_id:this.config.productId})})}catch{}this.sessionId=null}}async stop(){await this.cleanup()}}exports.CrowBrowserUse=u;exports.setPageController=p;
|