@eko-ai/eko 1.0.1-5.alpha-2
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/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/core/eko.d.ts +22 -0
- package/dist/core/tool-registry.d.ts +13 -0
- package/dist/extension/content/index.d.ts +16 -0
- package/dist/extension/core.d.ts +11 -0
- package/dist/extension/index.d.ts +7 -0
- package/dist/extension/script/bing.js +25 -0
- package/dist/extension/script/build_dom_tree.d.ts +38 -0
- package/dist/extension/script/build_dom_tree.js +662 -0
- package/dist/extension/script/common.js +212 -0
- package/dist/extension/script/duckduckgo.js +25 -0
- package/dist/extension/script/google.js +26 -0
- package/dist/extension/tools/browser.d.ts +22 -0
- package/dist/extension/tools/browser_use.d.ts +19 -0
- package/dist/extension/tools/element_click.d.ts +12 -0
- package/dist/extension/tools/export_file.d.ts +18 -0
- package/dist/extension/tools/extract_content.d.ts +18 -0
- package/dist/extension/tools/find_element_position.d.ts +12 -0
- package/dist/extension/tools/get_all_tabs.d.ts +9 -0
- package/dist/extension/tools/html_script.d.ts +10 -0
- package/dist/extension/tools/index.d.ts +13 -0
- package/dist/extension/tools/open_url.d.ts +18 -0
- package/dist/extension/tools/request_login.d.ts +10 -0
- package/dist/extension/tools/screenshot.d.ts +18 -0
- package/dist/extension/tools/tab_management.d.ts +19 -0
- package/dist/extension/tools/web_search.d.ts +18 -0
- package/dist/extension/utils.d.ts +31 -0
- package/dist/extension.cjs.js +2322 -0
- package/dist/extension.esm.js +2315 -0
- package/dist/extension_content_script.js +424 -0
- package/dist/fellou/computer.d.ts +20 -0
- package/dist/fellou/index.d.ts +6 -0
- package/dist/fellou/tools/computer_use.d.ts +18 -0
- package/dist/fellou.cjs.js +238 -0
- package/dist/fellou.esm.js +235 -0
- package/dist/index.cjs.js +9943 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.esm.js +9932 -0
- package/dist/models/action.d.ts +26 -0
- package/dist/models/workflow.d.ts +23 -0
- package/dist/nodejs/core.d.ts +2 -0
- package/dist/nodejs/index.d.ts +3 -0
- package/dist/nodejs/script/build_dom_tree.d.ts +1 -0
- package/dist/nodejs/tools/browser_use.d.ts +28 -0
- package/dist/nodejs/tools/command_execute.d.ts +12 -0
- package/dist/nodejs/tools/file_read.d.ts +11 -0
- package/dist/nodejs/tools/file_write.d.ts +15 -0
- package/dist/nodejs/tools/index.d.ts +5 -0
- package/dist/nodejs.cjs.js +71908 -0
- package/dist/nodejs.esm.js +71905 -0
- package/dist/schemas/workflow.schema.d.ts +77 -0
- package/dist/services/llm/claude-provider.d.ts +12 -0
- package/dist/services/llm/openai-provider.d.ts +12 -0
- package/dist/services/parser/workflow-parser.d.ts +23 -0
- package/dist/services/workflow/generator.d.ts +14 -0
- package/dist/services/workflow/templates.d.ts +8 -0
- package/dist/types/action.types.d.ts +45 -0
- package/dist/types/eko.types.d.ts +24 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/llm.types.d.ts +52 -0
- package/dist/types/parser.types.d.ts +9 -0
- package/dist/types/tools.types.d.ts +131 -0
- package/dist/types/workflow.types.d.ts +54 -0
- package/dist/universal_tools/cancel_workflow.d.ts +9 -0
- package/dist/universal_tools/human.d.ts +30 -0
- package/dist/universal_tools/index.d.ts +4 -0
- package/dist/universal_tools/summary_workflow.d.ts +9 -0
- package/dist/utils/execution-logger.d.ts +69 -0
- package/dist/web/core.d.ts +2 -0
- package/dist/web/index.d.ts +5 -0
- package/dist/web/script/build_dom_tree.d.ts +10 -0
- package/dist/web/tools/browser.d.ts +21 -0
- package/dist/web/tools/browser_use.d.ts +19 -0
- package/dist/web/tools/element_click.d.ts +12 -0
- package/dist/web/tools/export_file.d.ts +18 -0
- package/dist/web/tools/extract_content.d.ts +17 -0
- package/dist/web/tools/find_element_position.d.ts +12 -0
- package/dist/web/tools/html_script.d.ts +10 -0
- package/dist/web/tools/index.d.ts +8 -0
- package/dist/web/tools/screenshot.d.ts +18 -0
- package/dist/web.cjs.js +9651 -0
- package/dist/web.esm.js +9647 -0
- package/package.json +106 -0
|
@@ -0,0 +1,2315 @@
|
|
|
1
|
+
async function getWindowId(context) {
|
|
2
|
+
let windowId = context.variables.get('windowId');
|
|
3
|
+
if (windowId) {
|
|
4
|
+
try {
|
|
5
|
+
await chrome.windows.get(windowId);
|
|
6
|
+
}
|
|
7
|
+
catch (e) {
|
|
8
|
+
windowId = null;
|
|
9
|
+
context.variables.delete('windowId');
|
|
10
|
+
let tabId = context.variables.get('tabId');
|
|
11
|
+
if (tabId) {
|
|
12
|
+
try {
|
|
13
|
+
let tab = await chrome.tabs.get(tabId);
|
|
14
|
+
windowId = tab.windowId;
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
context.variables.delete('tabId');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (!windowId) {
|
|
23
|
+
const window = await chrome.windows.getCurrent();
|
|
24
|
+
windowId = window.id;
|
|
25
|
+
}
|
|
26
|
+
return windowId;
|
|
27
|
+
}
|
|
28
|
+
async function getTabId(context) {
|
|
29
|
+
let tabId = context.variables.get('tabId');
|
|
30
|
+
if (tabId) {
|
|
31
|
+
try {
|
|
32
|
+
await chrome.tabs.get(tabId);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
tabId = null;
|
|
36
|
+
context.variables.delete('tabId');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!tabId) {
|
|
40
|
+
let windowId = context.variables.get('windowId');
|
|
41
|
+
if (windowId) {
|
|
42
|
+
try {
|
|
43
|
+
tabId = await getCurrentTabId(windowId);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
tabId = await getCurrentTabId();
|
|
47
|
+
context.variables.delete('windowId');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
tabId = await getCurrentTabId();
|
|
52
|
+
}
|
|
53
|
+
if (!tabId) {
|
|
54
|
+
throw new Error('Could not find a valid tab');
|
|
55
|
+
}
|
|
56
|
+
context.variables.set('tabId', tabId);
|
|
57
|
+
}
|
|
58
|
+
return tabId;
|
|
59
|
+
}
|
|
60
|
+
function getCurrentTabId(windowId) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
chrome.tabs.query({ windowId, active: true, lastFocusedWindow: true }, function (tabs) {
|
|
63
|
+
if (chrome.runtime.lastError) {
|
|
64
|
+
console.error('Chrome runtime error:', chrome.runtime.lastError);
|
|
65
|
+
reject(chrome.runtime.lastError);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (tabs.length > 0) {
|
|
69
|
+
resolve(tabs[0].id);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
chrome.tabs.query({ windowId, active: true, currentWindow: true }, function (_tabs) {
|
|
73
|
+
if (_tabs.length > 0) {
|
|
74
|
+
resolve(_tabs[0].id);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
chrome.tabs.query({ windowId, status: 'complete', currentWindow: true }, function (__tabs) {
|
|
79
|
+
resolve(__tabs.length ? __tabs[__tabs.length - 1].id : undefined);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async function open_new_tab(url, newWindow, windowId) {
|
|
88
|
+
let tabId;
|
|
89
|
+
if (newWindow) {
|
|
90
|
+
let window = await chrome.windows.create({
|
|
91
|
+
type: 'normal',
|
|
92
|
+
state: 'maximized',
|
|
93
|
+
url: url,
|
|
94
|
+
});
|
|
95
|
+
windowId = window.id;
|
|
96
|
+
let tabs = window.tabs || [
|
|
97
|
+
await chrome.tabs.create({
|
|
98
|
+
url: url,
|
|
99
|
+
windowId: windowId,
|
|
100
|
+
}),
|
|
101
|
+
];
|
|
102
|
+
tabId = tabs[0].id;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
if (!windowId) {
|
|
106
|
+
const window = await chrome.windows.getCurrent();
|
|
107
|
+
windowId = window.id;
|
|
108
|
+
}
|
|
109
|
+
let tab = await chrome.tabs.create({
|
|
110
|
+
url: url,
|
|
111
|
+
windowId: windowId,
|
|
112
|
+
});
|
|
113
|
+
tabId = tab.id;
|
|
114
|
+
}
|
|
115
|
+
let tab = await waitForTabComplete(tabId);
|
|
116
|
+
await sleep(200);
|
|
117
|
+
return tab;
|
|
118
|
+
}
|
|
119
|
+
async function executeScript(tabId, func, args) {
|
|
120
|
+
let frameResults = await chrome.scripting.executeScript({
|
|
121
|
+
target: { tabId: tabId },
|
|
122
|
+
func: func,
|
|
123
|
+
args: args,
|
|
124
|
+
});
|
|
125
|
+
return frameResults[0].result;
|
|
126
|
+
}
|
|
127
|
+
async function waitForTabComplete(tabId, timeout = 15000) {
|
|
128
|
+
return new Promise(async (resolve, reject) => {
|
|
129
|
+
let tab = await chrome.tabs.get(tabId);
|
|
130
|
+
if (tab.status === 'complete') {
|
|
131
|
+
resolve(tab);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const time = setTimeout(() => {
|
|
135
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
136
|
+
reject();
|
|
137
|
+
}, timeout);
|
|
138
|
+
const listener = async (updatedTabId, changeInfo, tab) => {
|
|
139
|
+
if (updatedTabId === tabId && changeInfo.status === 'complete') {
|
|
140
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
141
|
+
clearTimeout(time);
|
|
142
|
+
resolve(tab);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
chrome.tabs.onUpdated.addListener(listener);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
async function doesTabExists(tabId) {
|
|
149
|
+
const tabExists = await new Promise((resolve) => {
|
|
150
|
+
chrome.tabs.get(tabId, (tab) => {
|
|
151
|
+
if (chrome.runtime.lastError) {
|
|
152
|
+
resolve(false);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
resolve(true);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
return tabExists;
|
|
160
|
+
}
|
|
161
|
+
async function getPageSize(tabId) {
|
|
162
|
+
if (!tabId) {
|
|
163
|
+
tabId = await getCurrentTabId();
|
|
164
|
+
}
|
|
165
|
+
let injectionResult = await chrome.scripting.executeScript({
|
|
166
|
+
target: { tabId: tabId },
|
|
167
|
+
func: () => [
|
|
168
|
+
window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
|
|
169
|
+
window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
|
|
170
|
+
],
|
|
171
|
+
});
|
|
172
|
+
return [injectionResult[0].result[0], injectionResult[0].result[1]];
|
|
173
|
+
}
|
|
174
|
+
function sleep(time) {
|
|
175
|
+
return new Promise((resolve) => setTimeout(() => resolve(), time));
|
|
176
|
+
}
|
|
177
|
+
async function injectScript(tabId, filename) {
|
|
178
|
+
let files = ['eko/script/common.js'];
|
|
179
|
+
if (filename) {
|
|
180
|
+
files.push('eko/script/' + filename);
|
|
181
|
+
}
|
|
182
|
+
await chrome.scripting.executeScript({
|
|
183
|
+
target: { tabId },
|
|
184
|
+
files: files,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
class MsgEvent {
|
|
188
|
+
constructor() {
|
|
189
|
+
this.eventMap = {};
|
|
190
|
+
}
|
|
191
|
+
addListener(callback, id) {
|
|
192
|
+
if (!id) {
|
|
193
|
+
id = new Date().getTime() + '' + Math.floor(Math.random() * 10000);
|
|
194
|
+
}
|
|
195
|
+
this.eventMap[id] = callback;
|
|
196
|
+
return id;
|
|
197
|
+
}
|
|
198
|
+
removeListener(id) {
|
|
199
|
+
delete this.eventMap[id];
|
|
200
|
+
}
|
|
201
|
+
async publish(msg) {
|
|
202
|
+
let values = Object.values(this.eventMap);
|
|
203
|
+
for (let i = 0; i < values.length; i++) {
|
|
204
|
+
try {
|
|
205
|
+
let result = values[i](msg);
|
|
206
|
+
if (isPromise(result)) {
|
|
207
|
+
await result;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
console.error(e);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Counter (Function: Wait for all asynchronous tasks to complete)
|
|
218
|
+
*/
|
|
219
|
+
class CountDownLatch {
|
|
220
|
+
constructor(count) {
|
|
221
|
+
this.resolve = undefined;
|
|
222
|
+
this.currentCount = count;
|
|
223
|
+
}
|
|
224
|
+
countDown() {
|
|
225
|
+
this.currentCount = this.currentCount - 1;
|
|
226
|
+
if (this.currentCount <= 0) {
|
|
227
|
+
this.resolve && this.resolve();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
await(timeout) {
|
|
231
|
+
const $this = this;
|
|
232
|
+
return new Promise((_resolve, reject) => {
|
|
233
|
+
let resolve = _resolve;
|
|
234
|
+
if (timeout > 0) {
|
|
235
|
+
let timeId = setTimeout(reject, timeout);
|
|
236
|
+
resolve = () => {
|
|
237
|
+
clearTimeout(timeId);
|
|
238
|
+
_resolve();
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
$this.resolve = resolve;
|
|
242
|
+
if ($this.currentCount <= 0) {
|
|
243
|
+
resolve();
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function isPromise(obj) {
|
|
249
|
+
return (!!obj &&
|
|
250
|
+
(typeof obj === 'object' || typeof obj === 'function') &&
|
|
251
|
+
typeof obj.then === 'function');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
var utils = /*#__PURE__*/Object.freeze({
|
|
255
|
+
__proto__: null,
|
|
256
|
+
CountDownLatch: CountDownLatch,
|
|
257
|
+
MsgEvent: MsgEvent,
|
|
258
|
+
doesTabExists: doesTabExists,
|
|
259
|
+
executeScript: executeScript,
|
|
260
|
+
getCurrentTabId: getCurrentTabId,
|
|
261
|
+
getPageSize: getPageSize,
|
|
262
|
+
getTabId: getTabId,
|
|
263
|
+
getWindowId: getWindowId,
|
|
264
|
+
injectScript: injectScript,
|
|
265
|
+
isPromise: isPromise,
|
|
266
|
+
open_new_tab: open_new_tab,
|
|
267
|
+
sleep: sleep,
|
|
268
|
+
waitForTabComplete: waitForTabComplete
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
async function type(tabId, text, coordinate) {
|
|
272
|
+
console.log('Sending type message to tab:', tabId, { text, coordinate });
|
|
273
|
+
try {
|
|
274
|
+
if (!coordinate) {
|
|
275
|
+
coordinate = (await cursor_position(tabId)).coordinate;
|
|
276
|
+
}
|
|
277
|
+
await mouse_move(tabId, coordinate);
|
|
278
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
279
|
+
type: 'computer:type',
|
|
280
|
+
text,
|
|
281
|
+
coordinate,
|
|
282
|
+
});
|
|
283
|
+
console.log('Got response:', response);
|
|
284
|
+
return response;
|
|
285
|
+
}
|
|
286
|
+
catch (e) {
|
|
287
|
+
console.error('Failed to send type message:', e);
|
|
288
|
+
throw e;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function type_by(tabId, text, xpath, highlightIndex) {
|
|
292
|
+
console.log('Sending type message to tab:', tabId, { text, xpath, highlightIndex });
|
|
293
|
+
try {
|
|
294
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
295
|
+
type: 'computer:type',
|
|
296
|
+
text,
|
|
297
|
+
xpath,
|
|
298
|
+
highlightIndex,
|
|
299
|
+
});
|
|
300
|
+
console.log('Got response:', response);
|
|
301
|
+
return response;
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
console.error('Failed to send type message:', e);
|
|
305
|
+
throw e;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function clear_input(tabId, coordinate) {
|
|
309
|
+
console.log('Sending clear_input message to tab:', tabId, { coordinate });
|
|
310
|
+
try {
|
|
311
|
+
if (!coordinate) {
|
|
312
|
+
coordinate = (await cursor_position(tabId)).coordinate;
|
|
313
|
+
}
|
|
314
|
+
await mouse_move(tabId, coordinate);
|
|
315
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
316
|
+
type: 'computer:type',
|
|
317
|
+
text: '',
|
|
318
|
+
coordinate,
|
|
319
|
+
});
|
|
320
|
+
console.log('Got response:', response);
|
|
321
|
+
return response;
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
console.error('Failed to send clear_input message:', e);
|
|
325
|
+
throw e;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function clear_input_by(tabId, xpath, highlightIndex) {
|
|
329
|
+
console.log('Sending clear_input_by message to tab:', tabId, { xpath, highlightIndex });
|
|
330
|
+
try {
|
|
331
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
332
|
+
type: 'computer:type',
|
|
333
|
+
text: '',
|
|
334
|
+
xpath,
|
|
335
|
+
highlightIndex,
|
|
336
|
+
});
|
|
337
|
+
console.log('Got response:', response);
|
|
338
|
+
return response;
|
|
339
|
+
}
|
|
340
|
+
catch (e) {
|
|
341
|
+
console.error('Failed to send clear_input_by message:', e);
|
|
342
|
+
throw e;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async function mouse_move(tabId, coordinate) {
|
|
346
|
+
console.log('Sending mouse_move message to tab:', tabId, { coordinate });
|
|
347
|
+
try {
|
|
348
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
349
|
+
type: 'computer:mouse_move',
|
|
350
|
+
coordinate,
|
|
351
|
+
});
|
|
352
|
+
console.log('Got response:', response);
|
|
353
|
+
return response;
|
|
354
|
+
}
|
|
355
|
+
catch (e) {
|
|
356
|
+
console.error('Failed to send mouse_move message:', e);
|
|
357
|
+
throw e;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async function left_click(tabId, coordinate) {
|
|
361
|
+
console.log('Sending left_click message to tab:', tabId, { coordinate });
|
|
362
|
+
try {
|
|
363
|
+
if (!coordinate) {
|
|
364
|
+
coordinate = (await cursor_position(tabId)).coordinate;
|
|
365
|
+
}
|
|
366
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
367
|
+
type: 'computer:left_click',
|
|
368
|
+
coordinate,
|
|
369
|
+
});
|
|
370
|
+
console.log('Got response:', response);
|
|
371
|
+
return response;
|
|
372
|
+
}
|
|
373
|
+
catch (e) {
|
|
374
|
+
console.error('Failed to send left_click message:', e);
|
|
375
|
+
throw e;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async function left_click_by(tabId, xpath, highlightIndex) {
|
|
379
|
+
console.log('Sending left_click_by message to tab:', tabId, { xpath, highlightIndex });
|
|
380
|
+
try {
|
|
381
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
382
|
+
type: 'computer:left_click',
|
|
383
|
+
xpath,
|
|
384
|
+
highlightIndex,
|
|
385
|
+
});
|
|
386
|
+
console.log('Got response:', response);
|
|
387
|
+
return response;
|
|
388
|
+
}
|
|
389
|
+
catch (e) {
|
|
390
|
+
console.error('Failed to send left_click_by message:', e);
|
|
391
|
+
throw e;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async function right_click(tabId, coordinate) {
|
|
395
|
+
console.log('Sending right_click message to tab:', tabId, { coordinate });
|
|
396
|
+
try {
|
|
397
|
+
if (!coordinate) {
|
|
398
|
+
coordinate = (await cursor_position(tabId)).coordinate;
|
|
399
|
+
}
|
|
400
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
401
|
+
type: 'computer:right_click',
|
|
402
|
+
coordinate,
|
|
403
|
+
});
|
|
404
|
+
console.log('Got response:', response);
|
|
405
|
+
return response;
|
|
406
|
+
}
|
|
407
|
+
catch (e) {
|
|
408
|
+
console.error('Failed to send right_click message:', e);
|
|
409
|
+
throw e;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async function right_click_by(tabId, xpath, highlightIndex) {
|
|
413
|
+
console.log('Sending right_click_by message to tab:', tabId, { xpath, highlightIndex });
|
|
414
|
+
try {
|
|
415
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
416
|
+
type: 'computer:right_click',
|
|
417
|
+
xpath,
|
|
418
|
+
highlightIndex,
|
|
419
|
+
});
|
|
420
|
+
console.log('Got response:', response);
|
|
421
|
+
return response;
|
|
422
|
+
}
|
|
423
|
+
catch (e) {
|
|
424
|
+
console.error('Failed to send right_click_by message:', e);
|
|
425
|
+
throw e;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async function double_click(tabId, coordinate) {
|
|
429
|
+
console.log('Sending double_click message to tab:', tabId, { coordinate });
|
|
430
|
+
try {
|
|
431
|
+
if (!coordinate) {
|
|
432
|
+
coordinate = (await cursor_position(tabId)).coordinate;
|
|
433
|
+
}
|
|
434
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
435
|
+
type: 'computer:double_click',
|
|
436
|
+
coordinate,
|
|
437
|
+
});
|
|
438
|
+
console.log('Got response:', response);
|
|
439
|
+
return response;
|
|
440
|
+
}
|
|
441
|
+
catch (e) {
|
|
442
|
+
console.error('Failed to send double_click message:', e);
|
|
443
|
+
throw e;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async function double_click_by(tabId, xpath, highlightIndex) {
|
|
447
|
+
console.log('Sending double_click_by message to tab:', tabId, { xpath, highlightIndex });
|
|
448
|
+
try {
|
|
449
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
450
|
+
type: 'computer:double_click',
|
|
451
|
+
xpath,
|
|
452
|
+
highlightIndex,
|
|
453
|
+
});
|
|
454
|
+
console.log('Got response:', response);
|
|
455
|
+
return response;
|
|
456
|
+
}
|
|
457
|
+
catch (e) {
|
|
458
|
+
console.error('Failed to send double_click_by message:', e);
|
|
459
|
+
throw e;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async function screenshot(windowId, compress) {
|
|
463
|
+
console.log('Taking screenshot of window:', windowId, { compress });
|
|
464
|
+
try {
|
|
465
|
+
let dataUrl;
|
|
466
|
+
if (compress) {
|
|
467
|
+
dataUrl = await chrome.tabs.captureVisibleTab(windowId, {
|
|
468
|
+
format: 'jpeg',
|
|
469
|
+
quality: 60, // 0-100
|
|
470
|
+
});
|
|
471
|
+
dataUrl = await compress_image(dataUrl, 0.7, 1);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
dataUrl = await chrome.tabs.captureVisibleTab(windowId, {
|
|
475
|
+
format: 'jpeg',
|
|
476
|
+
quality: 50,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
let data = dataUrl.substring(dataUrl.indexOf('base64,') + 7);
|
|
480
|
+
const result = {
|
|
481
|
+
image: {
|
|
482
|
+
type: 'base64',
|
|
483
|
+
media_type: dataUrl.indexOf('image/png') > -1 ? 'image/png' : 'image/jpeg',
|
|
484
|
+
data: data,
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
console.log('Got screenshot result:', result);
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
catch (e) {
|
|
491
|
+
console.error('Failed to take screenshot:', e);
|
|
492
|
+
throw e;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async function compress_image(dataUrl, scale = 0.8, quality = 0.8) {
|
|
496
|
+
console.log('Compressing image', { scale, quality });
|
|
497
|
+
try {
|
|
498
|
+
const bitmap = await createImageBitmap(await (await fetch(dataUrl)).blob());
|
|
499
|
+
let width = bitmap.width * scale;
|
|
500
|
+
let height = bitmap.height * scale;
|
|
501
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
502
|
+
const ctx = canvas.getContext('2d');
|
|
503
|
+
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
504
|
+
const blob = await canvas.convertToBlob({
|
|
505
|
+
type: 'image/jpeg',
|
|
506
|
+
quality: quality,
|
|
507
|
+
});
|
|
508
|
+
return new Promise((resolve) => {
|
|
509
|
+
const reader = new FileReader();
|
|
510
|
+
reader.onloadend = () => {
|
|
511
|
+
const result = reader.result;
|
|
512
|
+
console.log('Got compressed image result:', result);
|
|
513
|
+
resolve(result);
|
|
514
|
+
};
|
|
515
|
+
reader.readAsDataURL(blob);
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
catch (e) {
|
|
519
|
+
console.error('Failed to compress image:', e);
|
|
520
|
+
throw e;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async function scroll_to(tabId, coordinate) {
|
|
524
|
+
console.log('Sending scroll_to message to tab:', tabId, { coordinate });
|
|
525
|
+
try {
|
|
526
|
+
let from_coordinate = (await cursor_position(tabId)).coordinate;
|
|
527
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
528
|
+
type: 'computer:scroll_to',
|
|
529
|
+
from_coordinate,
|
|
530
|
+
to_coordinate: coordinate,
|
|
531
|
+
});
|
|
532
|
+
console.log('Got response:', response);
|
|
533
|
+
return response;
|
|
534
|
+
}
|
|
535
|
+
catch (e) {
|
|
536
|
+
console.error('Failed to send scroll_to message:', e);
|
|
537
|
+
throw e;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async function scroll_to_by(tabId, xpath, highlightIndex) {
|
|
541
|
+
console.log('Sending scroll_to_by message to tab:', tabId, { xpath, highlightIndex });
|
|
542
|
+
try {
|
|
543
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
544
|
+
type: 'computer:scroll_to',
|
|
545
|
+
xpath,
|
|
546
|
+
highlightIndex,
|
|
547
|
+
});
|
|
548
|
+
console.log('Got response:', response);
|
|
549
|
+
return response;
|
|
550
|
+
}
|
|
551
|
+
catch (e) {
|
|
552
|
+
console.error('Failed to send scroll_to_by message:', e);
|
|
553
|
+
throw e;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function get_dropdown_options(tabId, xpath, highlightIndex) {
|
|
557
|
+
console.log('Sending get_dropdown_options message to tab:', tabId, { xpath, highlightIndex });
|
|
558
|
+
try {
|
|
559
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
560
|
+
type: 'computer:get_dropdown_options',
|
|
561
|
+
xpath,
|
|
562
|
+
highlightIndex,
|
|
563
|
+
});
|
|
564
|
+
console.log('Got response:', response);
|
|
565
|
+
return response;
|
|
566
|
+
}
|
|
567
|
+
catch (e) {
|
|
568
|
+
console.error('Failed to send get_dropdown_options message:', e);
|
|
569
|
+
throw e;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async function select_dropdown_option(tabId, text, xpath, highlightIndex) {
|
|
573
|
+
console.log('Sending select_dropdown_option message to tab:', tabId, { text, xpath, highlightIndex });
|
|
574
|
+
try {
|
|
575
|
+
const response = await chrome.tabs.sendMessage(tabId, {
|
|
576
|
+
type: 'computer:select_dropdown_option',
|
|
577
|
+
text,
|
|
578
|
+
xpath,
|
|
579
|
+
highlightIndex,
|
|
580
|
+
});
|
|
581
|
+
console.log('Got response:', response);
|
|
582
|
+
return response;
|
|
583
|
+
}
|
|
584
|
+
catch (e) {
|
|
585
|
+
console.error('Failed to send select_dropdown_option message:', e);
|
|
586
|
+
throw e;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function cursor_position(tabId) {
|
|
590
|
+
console.log('Sending cursor_position message to tab:', tabId);
|
|
591
|
+
try {
|
|
592
|
+
let result = await chrome.tabs.sendMessage(tabId, {
|
|
593
|
+
type: 'computer:cursor_position',
|
|
594
|
+
});
|
|
595
|
+
console.log('Got cursor position:', result.coordinate);
|
|
596
|
+
return { coordinate: result.coordinate };
|
|
597
|
+
}
|
|
598
|
+
catch (e) {
|
|
599
|
+
console.error('Failed to send cursor_position message:', e);
|
|
600
|
+
throw e;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
async function size(tabId) {
|
|
604
|
+
console.log('Getting page size for tab:', tabId);
|
|
605
|
+
try {
|
|
606
|
+
const pageSize = await getPageSize(tabId);
|
|
607
|
+
console.log('Got page size:', pageSize);
|
|
608
|
+
return pageSize;
|
|
609
|
+
}
|
|
610
|
+
catch (e) {
|
|
611
|
+
console.error('Failed to get page size:', e);
|
|
612
|
+
throw e;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
var browser = /*#__PURE__*/Object.freeze({
|
|
617
|
+
__proto__: null,
|
|
618
|
+
clear_input: clear_input,
|
|
619
|
+
clear_input_by: clear_input_by,
|
|
620
|
+
compress_image: compress_image,
|
|
621
|
+
cursor_position: cursor_position,
|
|
622
|
+
double_click: double_click,
|
|
623
|
+
double_click_by: double_click_by,
|
|
624
|
+
get_dropdown_options: get_dropdown_options,
|
|
625
|
+
left_click: left_click,
|
|
626
|
+
left_click_by: left_click_by,
|
|
627
|
+
mouse_move: mouse_move,
|
|
628
|
+
right_click: right_click,
|
|
629
|
+
right_click_by: right_click_by,
|
|
630
|
+
screenshot: screenshot,
|
|
631
|
+
scroll_to: scroll_to,
|
|
632
|
+
scroll_to_by: scroll_to_by,
|
|
633
|
+
select_dropdown_option: select_dropdown_option,
|
|
634
|
+
size: size,
|
|
635
|
+
type: type,
|
|
636
|
+
type_by: type_by
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Browser Use for general
|
|
641
|
+
*/
|
|
642
|
+
class BrowserUse {
|
|
643
|
+
constructor() {
|
|
644
|
+
this.name = 'browser_use';
|
|
645
|
+
this.description = `Use structured commands to interact with the browser, manipulating page elements through screenshots and webpage element extraction.
|
|
646
|
+
* This is a browser GUI interface where you need to analyze webpages by taking screenshots and extracting page element structures, and specify action sequences to complete designated tasks.
|
|
647
|
+
* Before any operation, you must first call the \`screenshot_extract_element\` command, which will return the browser page screenshot and structured element information, both specially processed.
|
|
648
|
+
* ELEMENT INTERACTION:
|
|
649
|
+
- Only use indexes that exist in the provided element list
|
|
650
|
+
- Each element has a unique index number (e.g., "[33]:<button>")
|
|
651
|
+
- Elements marked with "[]:" are non-interactive (for context only)
|
|
652
|
+
* NAVIGATION & ERROR HANDLING:
|
|
653
|
+
- If no suitable elements exist, use other functions to complete the task
|
|
654
|
+
- If stuck, try alternative approaches
|
|
655
|
+
- Handle popups/cookies by accepting or closing them
|
|
656
|
+
- Use scroll to find elements you are looking for`;
|
|
657
|
+
this.input_schema = {
|
|
658
|
+
type: 'object',
|
|
659
|
+
properties: {
|
|
660
|
+
action: {
|
|
661
|
+
type: 'string',
|
|
662
|
+
description: `The action to perform. The available actions are:
|
|
663
|
+
* \`screenshot_extract_element\`: Take a screenshot of the web page and extract operable elements.
|
|
664
|
+
- Screenshots are used to understand page layouts, with labeled bounding boxes corresponding to element indexes. Each bounding box and its label share the same color, with labels typically positioned in the top-right corner of the box.
|
|
665
|
+
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
666
|
+
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
667
|
+
* \`input_text\`: Enter a string in the interactive element.
|
|
668
|
+
* \`click\`: Click to element.
|
|
669
|
+
* \`right_click\`: Right-click on the element.
|
|
670
|
+
* \`double_click\`: Double-click on the element.
|
|
671
|
+
* \`scroll_to\`: Scroll to the specified element.
|
|
672
|
+
* \`extract_content\`: Extract the text content of the current webpage.
|
|
673
|
+
* \`get_dropdown_options\`: Get all options from a native dropdown element.
|
|
674
|
+
* \`select_dropdown_option\`: Select dropdown option for interactive element index by the text of the option you want to select.`,
|
|
675
|
+
enum: [
|
|
676
|
+
'screenshot_extract_element',
|
|
677
|
+
'input_text',
|
|
678
|
+
'click',
|
|
679
|
+
'right_click',
|
|
680
|
+
'double_click',
|
|
681
|
+
'scroll_to',
|
|
682
|
+
'extract_content',
|
|
683
|
+
'get_dropdown_options',
|
|
684
|
+
'select_dropdown_option',
|
|
685
|
+
],
|
|
686
|
+
},
|
|
687
|
+
index: {
|
|
688
|
+
type: 'integer',
|
|
689
|
+
description: 'index of element, Operation elements must pass the corresponding index of the element',
|
|
690
|
+
},
|
|
691
|
+
text: {
|
|
692
|
+
type: 'string',
|
|
693
|
+
description: 'Required by `action=input_text` and `action=select_dropdown_option`',
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
required: ['action'],
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* browser
|
|
701
|
+
*
|
|
702
|
+
* @param {*} params { action: 'input_text', index: 1, text: 'string' }
|
|
703
|
+
* @returns > { success: true, image?: { type: 'base64', media_type: 'image/jpeg', data: '/9j...' }, text?: string }
|
|
704
|
+
*/
|
|
705
|
+
async execute(context, params) {
|
|
706
|
+
var _a;
|
|
707
|
+
try {
|
|
708
|
+
if (params === null || !params.action) {
|
|
709
|
+
throw new Error('Invalid parameters. Expected an object with a "action" property.');
|
|
710
|
+
}
|
|
711
|
+
let tabId;
|
|
712
|
+
try {
|
|
713
|
+
tabId = await getTabId(context);
|
|
714
|
+
if (!tabId || !Number.isInteger(tabId)) {
|
|
715
|
+
throw new Error('Could not get valid tab ID');
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
catch (e) {
|
|
719
|
+
console.error('Tab ID error:', e);
|
|
720
|
+
return { success: false, error: 'Could not access browser tab' };
|
|
721
|
+
}
|
|
722
|
+
let windowId = await getWindowId(context);
|
|
723
|
+
let selector_map = context.selector_map;
|
|
724
|
+
let selector_xpath;
|
|
725
|
+
if (params.index != null && selector_map) {
|
|
726
|
+
selector_xpath = (_a = selector_map[params.index]) === null || _a === void 0 ? void 0 : _a.xpath;
|
|
727
|
+
if (!selector_xpath) {
|
|
728
|
+
throw new Error('Element does not exist');
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
let result;
|
|
732
|
+
switch (params.action) {
|
|
733
|
+
case 'input_text':
|
|
734
|
+
if (params.index == null) {
|
|
735
|
+
throw new Error('index parameter is required');
|
|
736
|
+
}
|
|
737
|
+
if (params.text == null) {
|
|
738
|
+
throw new Error('text parameter is required');
|
|
739
|
+
}
|
|
740
|
+
await clear_input_by(tabId, selector_xpath, params.index);
|
|
741
|
+
result = await type_by(tabId, params.text, selector_xpath, params.index);
|
|
742
|
+
await sleep(200);
|
|
743
|
+
break;
|
|
744
|
+
case 'click':
|
|
745
|
+
if (params.index == null) {
|
|
746
|
+
throw new Error('index parameter is required');
|
|
747
|
+
}
|
|
748
|
+
result = await left_click_by(tabId, selector_xpath, params.index);
|
|
749
|
+
await sleep(100);
|
|
750
|
+
break;
|
|
751
|
+
case 'right_click':
|
|
752
|
+
if (params.index == null) {
|
|
753
|
+
throw new Error('index parameter is required');
|
|
754
|
+
}
|
|
755
|
+
result = await right_click_by(tabId, selector_xpath, params.index);
|
|
756
|
+
await sleep(100);
|
|
757
|
+
break;
|
|
758
|
+
case 'double_click':
|
|
759
|
+
if (params.index == null) {
|
|
760
|
+
throw new Error('index parameter is required');
|
|
761
|
+
}
|
|
762
|
+
result = await double_click_by(tabId, selector_xpath, params.index);
|
|
763
|
+
await sleep(100);
|
|
764
|
+
break;
|
|
765
|
+
case 'scroll_to':
|
|
766
|
+
if (params.index == null) {
|
|
767
|
+
throw new Error('index parameter is required');
|
|
768
|
+
}
|
|
769
|
+
result = await scroll_to_by(tabId, selector_xpath, params.index);
|
|
770
|
+
await sleep(500);
|
|
771
|
+
break;
|
|
772
|
+
case 'extract_content':
|
|
773
|
+
let tab = await chrome.tabs.get(tabId);
|
|
774
|
+
await injectScript(tabId);
|
|
775
|
+
await sleep(200);
|
|
776
|
+
let content = await executeScript(tabId, () => {
|
|
777
|
+
return eko.extractHtmlContent();
|
|
778
|
+
}, []);
|
|
779
|
+
result = {
|
|
780
|
+
title: tab.title,
|
|
781
|
+
url: tab.url,
|
|
782
|
+
content: content,
|
|
783
|
+
};
|
|
784
|
+
break;
|
|
785
|
+
case 'get_dropdown_options':
|
|
786
|
+
if (params.index == null) {
|
|
787
|
+
throw new Error('index parameter is required');
|
|
788
|
+
}
|
|
789
|
+
result = await get_dropdown_options(tabId, selector_xpath, params.index);
|
|
790
|
+
break;
|
|
791
|
+
case 'select_dropdown_option':
|
|
792
|
+
if (params.index == null) {
|
|
793
|
+
throw new Error('index parameter is required');
|
|
794
|
+
}
|
|
795
|
+
if (params.text == null) {
|
|
796
|
+
throw new Error('text parameter is required');
|
|
797
|
+
}
|
|
798
|
+
result = await select_dropdown_option(tabId, params.text, selector_xpath, params.index);
|
|
799
|
+
break;
|
|
800
|
+
case 'screenshot_extract_element':
|
|
801
|
+
await sleep(100);
|
|
802
|
+
await injectScript(tabId, 'build_dom_tree.js');
|
|
803
|
+
await sleep(100);
|
|
804
|
+
let element_result = await executeScript(tabId, () => {
|
|
805
|
+
return window.get_clickable_elements(true);
|
|
806
|
+
}, []);
|
|
807
|
+
context.selector_map = element_result.selector_map;
|
|
808
|
+
let screenshot$1 = await screenshot(windowId, true);
|
|
809
|
+
await executeScript(tabId, () => {
|
|
810
|
+
return window.remove_highlight();
|
|
811
|
+
}, []);
|
|
812
|
+
result = { image: screenshot$1.image, text: element_result.element_str };
|
|
813
|
+
break;
|
|
814
|
+
default:
|
|
815
|
+
throw Error(`Invalid parameters. The "${params.action}" value is not included in the "action" enumeration.`);
|
|
816
|
+
}
|
|
817
|
+
if (result) {
|
|
818
|
+
return { success: true, ...result };
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
return { success: false };
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
catch (e) {
|
|
825
|
+
console.error('Browser use error:', e);
|
|
826
|
+
return { success: false, error: e === null || e === undefined ? undefined : e.message };
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
destroy(context) {
|
|
830
|
+
delete context.selector_map;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function exportFile(filename, type, content) {
|
|
835
|
+
const blob = new Blob([content], { type: type });
|
|
836
|
+
const link = document.createElement('a');
|
|
837
|
+
link.href = URL.createObjectURL(blob);
|
|
838
|
+
link.download = filename;
|
|
839
|
+
document.body.appendChild(link);
|
|
840
|
+
link.click();
|
|
841
|
+
document.body.removeChild(link);
|
|
842
|
+
URL.revokeObjectURL(link.href);
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Extract the elements related to html operability and wrap them into pseudo-html code.
|
|
846
|
+
*/
|
|
847
|
+
function extractOperableElements() {
|
|
848
|
+
// visible
|
|
849
|
+
const isElementVisible = (element) => {
|
|
850
|
+
const style = window.getComputedStyle(element);
|
|
851
|
+
return (style.display !== 'none' &&
|
|
852
|
+
style.visibility !== 'hidden' &&
|
|
853
|
+
style.opacity !== '0' &&
|
|
854
|
+
element.offsetWidth > 0 &&
|
|
855
|
+
element.offsetHeight > 0);
|
|
856
|
+
};
|
|
857
|
+
// element original index
|
|
858
|
+
const getElementIndex = (element) => {
|
|
859
|
+
const xpath = document.evaluate('preceding::*', element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
860
|
+
return xpath.snapshotLength;
|
|
861
|
+
};
|
|
862
|
+
// exclude
|
|
863
|
+
const addExclude = (excludes, children) => {
|
|
864
|
+
for (let i = 0; i < children.length; i++) {
|
|
865
|
+
excludes.push(children[i]);
|
|
866
|
+
if (children[i].children) {
|
|
867
|
+
addExclude(excludes, children[i].children);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
// { pseudoId: element }
|
|
872
|
+
let elementMap = {};
|
|
873
|
+
let nextId = 1;
|
|
874
|
+
let elements = [];
|
|
875
|
+
let excludes = [];
|
|
876
|
+
// operable element
|
|
877
|
+
const operableSelectors = 'a, button, input, textarea, select';
|
|
878
|
+
document.querySelectorAll(operableSelectors).forEach((element) => {
|
|
879
|
+
if (isElementVisible(element) && excludes.indexOf(element) == -1) {
|
|
880
|
+
const id = nextId++;
|
|
881
|
+
elementMap[id.toString()] = element;
|
|
882
|
+
const tagName = element.tagName.toLowerCase();
|
|
883
|
+
const attributes = Array.from(element.attributes)
|
|
884
|
+
.filter((attr) => ['id', 'name', 'type', 'value', 'href', 'title', 'placeholder'].includes(attr.name))
|
|
885
|
+
.map((attr) => `${attr.name == 'id' ? 'target' : attr.name}="${attr.value}"`)
|
|
886
|
+
.join(' ');
|
|
887
|
+
elements.push({
|
|
888
|
+
originalIndex: getElementIndex(element),
|
|
889
|
+
id: id,
|
|
890
|
+
html: `<${tagName} id="${id}" ${attributes}>${tagName == 'select' ? element.innerHTML : element.innerText || ''}</${tagName}>`,
|
|
891
|
+
});
|
|
892
|
+
addExclude(excludes, element.children);
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
// short text element
|
|
896
|
+
const textWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
|
|
897
|
+
acceptNode: function (node) {
|
|
898
|
+
var _a;
|
|
899
|
+
if (node.matches(operableSelectors) || excludes.indexOf(node) != -1) {
|
|
900
|
+
// skip
|
|
901
|
+
return NodeFilter.FILTER_SKIP;
|
|
902
|
+
}
|
|
903
|
+
// text <= 100
|
|
904
|
+
const text = (_a = node.innerText) === null || _a === undefined ? undefined : _a.trim();
|
|
905
|
+
if (isElementVisible(node) &&
|
|
906
|
+
text &&
|
|
907
|
+
text.length <= 100 &&
|
|
908
|
+
text.length > 0 &&
|
|
909
|
+
node.children.length === 0) {
|
|
910
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
911
|
+
}
|
|
912
|
+
// skip
|
|
913
|
+
return NodeFilter.FILTER_SKIP;
|
|
914
|
+
},
|
|
915
|
+
});
|
|
916
|
+
let currentNode;
|
|
917
|
+
while ((currentNode = textWalker.nextNode())) {
|
|
918
|
+
const id = nextId++;
|
|
919
|
+
elementMap[id.toString()] = currentNode;
|
|
920
|
+
const tagName = currentNode.tagName.toLowerCase();
|
|
921
|
+
elements.push({
|
|
922
|
+
originalIndex: getElementIndex(currentNode),
|
|
923
|
+
id: id,
|
|
924
|
+
html: `<${tagName} id="${id}">${currentNode.innerText.trim()}</${tagName}>`,
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
// element sort
|
|
928
|
+
elements.sort((a, b) => a.originalIndex - b.originalIndex);
|
|
929
|
+
// cache
|
|
930
|
+
window.operableElementMap = elementMap;
|
|
931
|
+
// pseudo html
|
|
932
|
+
return elements.map((e) => e.html).join('\n');
|
|
933
|
+
}
|
|
934
|
+
function clickOperableElement(id) {
|
|
935
|
+
let element = window.operableElementMap[id];
|
|
936
|
+
if (!element) {
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
if (element.click) {
|
|
940
|
+
element.click();
|
|
941
|
+
}
|
|
942
|
+
else {
|
|
943
|
+
element.dispatchEvent(new MouseEvent('click', {
|
|
944
|
+
view: window,
|
|
945
|
+
bubbles: true,
|
|
946
|
+
cancelable: true,
|
|
947
|
+
}));
|
|
948
|
+
}
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
function getOperableElementRect(id) {
|
|
952
|
+
let element = window.operableElementMap[id];
|
|
953
|
+
if (!element) {
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
const rect = element.getBoundingClientRect();
|
|
957
|
+
return {
|
|
958
|
+
left: rect.left + window.scrollX,
|
|
959
|
+
top: rect.top + window.scrollY,
|
|
960
|
+
right: rect.right + window.scrollX,
|
|
961
|
+
bottom: rect.bottom + window.scrollY,
|
|
962
|
+
width: rect.right - rect.left,
|
|
963
|
+
height: rect.bottom - rect.top,
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Element click
|
|
969
|
+
*/
|
|
970
|
+
class ElementClick {
|
|
971
|
+
constructor() {
|
|
972
|
+
this.name = 'element_click';
|
|
973
|
+
this.description = 'Click the element through task prompts';
|
|
974
|
+
this.input_schema = {
|
|
975
|
+
type: 'object',
|
|
976
|
+
properties: {
|
|
977
|
+
task_prompt: {
|
|
978
|
+
type: 'string',
|
|
979
|
+
description: 'Task prompt, eg: click search button',
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
required: ['task_prompt'],
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
async execute(context, params) {
|
|
986
|
+
if (typeof params !== 'object' || params === null || !params.task_prompt) {
|
|
987
|
+
throw new Error('Invalid parameters. Expected an object with a "task_prompt" property.');
|
|
988
|
+
}
|
|
989
|
+
let result;
|
|
990
|
+
let task_prompt = params.task_prompt;
|
|
991
|
+
try {
|
|
992
|
+
result = await executeWithHtmlElement$1(context, task_prompt);
|
|
993
|
+
}
|
|
994
|
+
catch (e) {
|
|
995
|
+
console.log(e);
|
|
996
|
+
result = false;
|
|
997
|
+
}
|
|
998
|
+
if (!result) {
|
|
999
|
+
result = await executeWithBrowserUse$1(context, task_prompt);
|
|
1000
|
+
}
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
async function executeWithHtmlElement$1(context, task_prompt) {
|
|
1005
|
+
let tabId = await getTabId(context);
|
|
1006
|
+
let pseudoHtml = await executeScript(tabId, extractOperableElements, []);
|
|
1007
|
+
let messages = [
|
|
1008
|
+
{
|
|
1009
|
+
role: 'user',
|
|
1010
|
+
content: `# Task
|
|
1011
|
+
Determine the operation intent based on user input, find the element ID that the user needs to operate on in the webpage HTML, and if the element does not exist, do nothing.
|
|
1012
|
+
Output JSON format, no explanation required.
|
|
1013
|
+
|
|
1014
|
+
# User input
|
|
1015
|
+
${task_prompt}
|
|
1016
|
+
|
|
1017
|
+
# Output example (when the element exists)
|
|
1018
|
+
{"elementId": "1", "operationType": "click"}
|
|
1019
|
+
|
|
1020
|
+
# Output example (when the element does not exist)
|
|
1021
|
+
{"elementId": null, "operationType": "unknown"}
|
|
1022
|
+
|
|
1023
|
+
# HTML
|
|
1024
|
+
${pseudoHtml}
|
|
1025
|
+
`,
|
|
1026
|
+
},
|
|
1027
|
+
];
|
|
1028
|
+
let llm_params = { maxTokens: 1024 };
|
|
1029
|
+
let response = await context.llmProvider.generateText(messages, llm_params);
|
|
1030
|
+
let content = typeof response.content == 'string' ? response.content : response.content[0].text;
|
|
1031
|
+
let json = content.substring(content.indexOf('{'), content.indexOf('}') + 1);
|
|
1032
|
+
let elementId = JSON.parse(json).elementId;
|
|
1033
|
+
if (elementId) {
|
|
1034
|
+
return await executeScript(tabId, clickOperableElement, [elementId]);
|
|
1035
|
+
}
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
async function executeWithBrowserUse$1(context, task_prompt) {
|
|
1039
|
+
let tabId = await getTabId(context);
|
|
1040
|
+
let windowId = await getWindowId(context);
|
|
1041
|
+
let screenshot_result = await screenshot(windowId, false);
|
|
1042
|
+
let messages = [
|
|
1043
|
+
{
|
|
1044
|
+
role: 'user',
|
|
1045
|
+
content: [
|
|
1046
|
+
{
|
|
1047
|
+
type: 'image',
|
|
1048
|
+
source: screenshot_result.image,
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
type: 'text',
|
|
1052
|
+
text: 'click: ' + task_prompt,
|
|
1053
|
+
},
|
|
1054
|
+
],
|
|
1055
|
+
},
|
|
1056
|
+
];
|
|
1057
|
+
let llm_params = {
|
|
1058
|
+
maxTokens: 1024,
|
|
1059
|
+
toolChoice: {
|
|
1060
|
+
type: 'tool',
|
|
1061
|
+
name: 'left_click',
|
|
1062
|
+
},
|
|
1063
|
+
tools: [
|
|
1064
|
+
{
|
|
1065
|
+
name: 'left_click',
|
|
1066
|
+
description: 'click element',
|
|
1067
|
+
input_schema: {
|
|
1068
|
+
type: 'object',
|
|
1069
|
+
properties: {
|
|
1070
|
+
coordinate: {
|
|
1071
|
+
type: 'array',
|
|
1072
|
+
description: '(x, y): The x (pixels from the left edge) and y (pixels from the top edge) coordinates.',
|
|
1073
|
+
},
|
|
1074
|
+
},
|
|
1075
|
+
required: ['coordinate'],
|
|
1076
|
+
},
|
|
1077
|
+
},
|
|
1078
|
+
],
|
|
1079
|
+
};
|
|
1080
|
+
let response = await context.llmProvider.generateText(messages, llm_params);
|
|
1081
|
+
let input = response.toolCalls[0].input;
|
|
1082
|
+
let coordinate = input.coordinate;
|
|
1083
|
+
let click_result = await left_click(tabId, coordinate);
|
|
1084
|
+
return click_result;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* Export file
|
|
1089
|
+
*/
|
|
1090
|
+
class ExportFile {
|
|
1091
|
+
constructor() {
|
|
1092
|
+
this.name = 'export_file';
|
|
1093
|
+
this.description = 'Content exported as a file, support text format';
|
|
1094
|
+
this.input_schema = {
|
|
1095
|
+
type: 'object',
|
|
1096
|
+
properties: {
|
|
1097
|
+
fileType: {
|
|
1098
|
+
type: 'string',
|
|
1099
|
+
description: 'File format type',
|
|
1100
|
+
enum: ['txt', 'csv', 'md', 'html', 'js', 'xml', 'json', 'yml', 'sql'],
|
|
1101
|
+
},
|
|
1102
|
+
content: {
|
|
1103
|
+
type: 'string',
|
|
1104
|
+
description: 'Export file content',
|
|
1105
|
+
},
|
|
1106
|
+
filename: {
|
|
1107
|
+
type: 'string',
|
|
1108
|
+
description: 'File name',
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
required: ['fileType', 'content'],
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* export
|
|
1116
|
+
*
|
|
1117
|
+
* @param {*} params { fileType: 'csv', content: 'field1,field2\ndata1,data2' }
|
|
1118
|
+
* @returns > { success: true }
|
|
1119
|
+
*/
|
|
1120
|
+
async execute(context, params) {
|
|
1121
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1122
|
+
if (typeof params !== 'object' || params === null || !('content' in params)) {
|
|
1123
|
+
throw new Error('Invalid parameters. Expected an object with a "content" property.');
|
|
1124
|
+
}
|
|
1125
|
+
await ((_c = (_b = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks) === null || _b === undefined ? undefined : _b.onExportFile) === null || _c === undefined ? undefined : _c.call(_b, params));
|
|
1126
|
+
let type = 'text/plain';
|
|
1127
|
+
switch (params.fileType) {
|
|
1128
|
+
case 'csv':
|
|
1129
|
+
type = 'text/csv';
|
|
1130
|
+
break;
|
|
1131
|
+
case 'md':
|
|
1132
|
+
type = 'text/markdown';
|
|
1133
|
+
break;
|
|
1134
|
+
case 'html':
|
|
1135
|
+
type = 'text/html';
|
|
1136
|
+
break;
|
|
1137
|
+
case 'js':
|
|
1138
|
+
type = 'application/javascript';
|
|
1139
|
+
break;
|
|
1140
|
+
case 'xml':
|
|
1141
|
+
type = 'text/xml';
|
|
1142
|
+
break;
|
|
1143
|
+
case 'json':
|
|
1144
|
+
type = 'application/json';
|
|
1145
|
+
break;
|
|
1146
|
+
}
|
|
1147
|
+
let filename;
|
|
1148
|
+
if (!params.filename) {
|
|
1149
|
+
filename = new Date().getTime() + '.' + params.fileType;
|
|
1150
|
+
}
|
|
1151
|
+
else if (!(params.filename + '').endsWith(params.fileType)) {
|
|
1152
|
+
filename = params.filename + '.' + params.fileType;
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
filename = params.filename;
|
|
1156
|
+
}
|
|
1157
|
+
try {
|
|
1158
|
+
let tabId = await getTabId(context);
|
|
1159
|
+
await chrome.scripting.executeScript({
|
|
1160
|
+
target: { tabId: tabId },
|
|
1161
|
+
func: exportFile,
|
|
1162
|
+
args: [filename, type, params.content],
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
catch (e) {
|
|
1166
|
+
let tab;
|
|
1167
|
+
const url = 'https://www.google.com';
|
|
1168
|
+
if (context.ekoConfig.workingWindowId) {
|
|
1169
|
+
tab = await open_new_tab(url, false, context.ekoConfig.workingWindowId);
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
tab = await open_new_tab(url, true);
|
|
1173
|
+
}
|
|
1174
|
+
(_f = (_e = (_d = context.callback) === null || _d === undefined ? undefined : _d.hooks) === null || _e === undefined ? undefined : _e.onTabCreated) === null || _f === undefined ? undefined : _f.call(_e, tab.id);
|
|
1175
|
+
let tabId = tab.id;
|
|
1176
|
+
await chrome.scripting.executeScript({
|
|
1177
|
+
target: { tabId: tabId },
|
|
1178
|
+
func: exportFile,
|
|
1179
|
+
args: [filename, type, params.content],
|
|
1180
|
+
});
|
|
1181
|
+
await sleep(5000);
|
|
1182
|
+
await chrome.tabs.remove(tabId);
|
|
1183
|
+
}
|
|
1184
|
+
return { success: true };
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* Extract Page Content
|
|
1190
|
+
*/
|
|
1191
|
+
class ExtractContent {
|
|
1192
|
+
constructor() {
|
|
1193
|
+
this.name = 'extract_content';
|
|
1194
|
+
this.description = 'Extract the text content of the current webpage';
|
|
1195
|
+
this.input_schema = {
|
|
1196
|
+
type: 'object',
|
|
1197
|
+
properties: {},
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Extract Page Content
|
|
1202
|
+
*
|
|
1203
|
+
* @param {*} params {}
|
|
1204
|
+
* @returns > { tabId, result: { title, url, content }, success: true }
|
|
1205
|
+
*/
|
|
1206
|
+
async execute(context, params) {
|
|
1207
|
+
let tabId = await getTabId(context);
|
|
1208
|
+
let tab = await chrome.tabs.get(tabId);
|
|
1209
|
+
await injectScript(tabId);
|
|
1210
|
+
await sleep(500);
|
|
1211
|
+
let content = await executeScript(tabId, () => {
|
|
1212
|
+
return eko.extractHtmlContent();
|
|
1213
|
+
}, []);
|
|
1214
|
+
return {
|
|
1215
|
+
tabId,
|
|
1216
|
+
result: {
|
|
1217
|
+
title: tab.title,
|
|
1218
|
+
url: tab.url,
|
|
1219
|
+
content: content,
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Find Element Position
|
|
1227
|
+
*/
|
|
1228
|
+
class FindElementPosition {
|
|
1229
|
+
constructor() {
|
|
1230
|
+
this.name = 'find_element_position';
|
|
1231
|
+
this.description = 'Locate Element Coordinates through Task Prompts';
|
|
1232
|
+
this.input_schema = {
|
|
1233
|
+
type: 'object',
|
|
1234
|
+
properties: {
|
|
1235
|
+
task_prompt: {
|
|
1236
|
+
type: 'string',
|
|
1237
|
+
description: 'Task prompt, eg: find the search input box',
|
|
1238
|
+
},
|
|
1239
|
+
},
|
|
1240
|
+
required: ['task_prompt'],
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
async execute(context, params) {
|
|
1244
|
+
if (typeof params !== 'object' || params === null || !params.task_prompt) {
|
|
1245
|
+
throw new Error('Invalid parameters. Expected an object with a "task_prompt" property.');
|
|
1246
|
+
}
|
|
1247
|
+
let result;
|
|
1248
|
+
let task_prompt = params.task_prompt;
|
|
1249
|
+
try {
|
|
1250
|
+
result = await executeWithHtmlElement(context, task_prompt);
|
|
1251
|
+
}
|
|
1252
|
+
catch (e) {
|
|
1253
|
+
console.log(e);
|
|
1254
|
+
result = null;
|
|
1255
|
+
}
|
|
1256
|
+
if (!result) {
|
|
1257
|
+
result = await executeWithBrowserUse(context, task_prompt);
|
|
1258
|
+
}
|
|
1259
|
+
return result;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
async function executeWithHtmlElement(context, task_prompt) {
|
|
1263
|
+
let tabId = await getTabId(context);
|
|
1264
|
+
let pseudoHtml = await executeScript(tabId, extractOperableElements, []);
|
|
1265
|
+
let messages = [
|
|
1266
|
+
{
|
|
1267
|
+
role: 'user',
|
|
1268
|
+
content: `# Task
|
|
1269
|
+
Find the element ID that the user needs to operate on in the webpage HTML, and if the element does not exist, do nothing.
|
|
1270
|
+
Output JSON format, no explanation required.
|
|
1271
|
+
|
|
1272
|
+
# User input
|
|
1273
|
+
${task_prompt}
|
|
1274
|
+
|
|
1275
|
+
# Output example (when the element exists)
|
|
1276
|
+
{"elementId": "1"}
|
|
1277
|
+
|
|
1278
|
+
# Output example (when the element does not exist)
|
|
1279
|
+
{"elementId": null}
|
|
1280
|
+
|
|
1281
|
+
# HTML
|
|
1282
|
+
${pseudoHtml}
|
|
1283
|
+
`,
|
|
1284
|
+
},
|
|
1285
|
+
];
|
|
1286
|
+
let llm_params = { maxTokens: 1024 };
|
|
1287
|
+
let response = await context.llmProvider.generateText(messages, llm_params);
|
|
1288
|
+
let content = typeof response.content == 'string' ? response.content : response.content[0].text;
|
|
1289
|
+
let json = content.substring(content.indexOf('{'), content.indexOf('}') + 1);
|
|
1290
|
+
let elementId = JSON.parse(json).elementId;
|
|
1291
|
+
if (elementId) {
|
|
1292
|
+
return await executeScript(tabId, getOperableElementRect, [elementId]);
|
|
1293
|
+
}
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
async function executeWithBrowserUse(context, task_prompt) {
|
|
1297
|
+
await getTabId(context);
|
|
1298
|
+
let windowId = await getWindowId(context);
|
|
1299
|
+
let screenshot_result = await screenshot(windowId, false);
|
|
1300
|
+
let messages = [
|
|
1301
|
+
{
|
|
1302
|
+
role: 'user',
|
|
1303
|
+
content: [
|
|
1304
|
+
{
|
|
1305
|
+
type: 'image',
|
|
1306
|
+
source: screenshot_result.image,
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
type: 'text',
|
|
1310
|
+
text: 'Find the element: ' + task_prompt,
|
|
1311
|
+
},
|
|
1312
|
+
],
|
|
1313
|
+
},
|
|
1314
|
+
];
|
|
1315
|
+
let llm_params = {
|
|
1316
|
+
maxTokens: 1024,
|
|
1317
|
+
toolChoice: {
|
|
1318
|
+
type: 'tool',
|
|
1319
|
+
name: 'get_element_by_coordinate',
|
|
1320
|
+
},
|
|
1321
|
+
tools: [
|
|
1322
|
+
{
|
|
1323
|
+
name: 'get_element_by_coordinate',
|
|
1324
|
+
description: 'Retrieve element information based on coordinate',
|
|
1325
|
+
input_schema: {
|
|
1326
|
+
type: 'object',
|
|
1327
|
+
properties: {
|
|
1328
|
+
coordinate: {
|
|
1329
|
+
type: 'array',
|
|
1330
|
+
description: '(x, y): The x (pixels from the left edge) and y (pixels from the top edge) coordinates.',
|
|
1331
|
+
},
|
|
1332
|
+
},
|
|
1333
|
+
required: ['coordinate'],
|
|
1334
|
+
},
|
|
1335
|
+
},
|
|
1336
|
+
],
|
|
1337
|
+
};
|
|
1338
|
+
let response = await context.llmProvider.generateText(messages, llm_params);
|
|
1339
|
+
let input = response.toolCalls[0].input;
|
|
1340
|
+
let coordinate = input.coordinate;
|
|
1341
|
+
return {
|
|
1342
|
+
left: coordinate[0],
|
|
1343
|
+
top: coordinate[1],
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
class GetAllTabs {
|
|
1348
|
+
constructor() {
|
|
1349
|
+
this.name = 'get_all_tabs';
|
|
1350
|
+
this.description = 'Get the tabId, title, url and content from current all tabs without opening new tab.';
|
|
1351
|
+
this.input_schema = {
|
|
1352
|
+
type: 'object',
|
|
1353
|
+
properties: {},
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
async execute(context, params) {
|
|
1357
|
+
const currentWindow = await chrome.windows.getCurrent();
|
|
1358
|
+
const windowId = currentWindow.id;
|
|
1359
|
+
const tabs = await chrome.tabs.query({ windowId });
|
|
1360
|
+
const tabsInfo = [];
|
|
1361
|
+
for (const tab of tabs) {
|
|
1362
|
+
if (tab.id === undefined) {
|
|
1363
|
+
console.warn(`Tab ID is undefined for tab with URL: ${tab.url}`);
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
await injectScript(tab.id);
|
|
1367
|
+
await sleep(500);
|
|
1368
|
+
let content = await executeScript(tab.id, () => {
|
|
1369
|
+
return eko.extractHtmlContent();
|
|
1370
|
+
}, []);
|
|
1371
|
+
// Use title as description, but requirement may evolve
|
|
1372
|
+
let description = tab.title ? tab.title : "No description available.";
|
|
1373
|
+
const tabInfo = {
|
|
1374
|
+
id: tab.id,
|
|
1375
|
+
url: tab.url,
|
|
1376
|
+
title: tab.title,
|
|
1377
|
+
content: content,
|
|
1378
|
+
description: description,
|
|
1379
|
+
};
|
|
1380
|
+
console.log("url: " + tab.url);
|
|
1381
|
+
console.log("title: " + tab.title);
|
|
1382
|
+
console.log("description: " + description);
|
|
1383
|
+
tabsInfo.push(tabInfo);
|
|
1384
|
+
}
|
|
1385
|
+
return tabsInfo;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* Open Url
|
|
1391
|
+
*/
|
|
1392
|
+
class OpenUrl {
|
|
1393
|
+
constructor() {
|
|
1394
|
+
this.name = 'open_url';
|
|
1395
|
+
this.description = 'Open the specified URL link in browser window';
|
|
1396
|
+
this.input_schema = {
|
|
1397
|
+
type: 'object',
|
|
1398
|
+
properties: {
|
|
1399
|
+
url: {
|
|
1400
|
+
type: 'string',
|
|
1401
|
+
description: 'URL link address',
|
|
1402
|
+
},
|
|
1403
|
+
newWindow: {
|
|
1404
|
+
type: 'boolean',
|
|
1405
|
+
description: 'true: Open in a new window; false: Open in the current window.',
|
|
1406
|
+
},
|
|
1407
|
+
},
|
|
1408
|
+
required: ['url'],
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Open Url
|
|
1413
|
+
*
|
|
1414
|
+
* @param {*} params { url: 'https://www.google.com', newWindow: true }
|
|
1415
|
+
* @returns > { tabId, windowId, title, success: true }
|
|
1416
|
+
*/
|
|
1417
|
+
async execute(context, params) {
|
|
1418
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1419
|
+
if (typeof params !== 'object' || params === null || !params.url) {
|
|
1420
|
+
throw new Error('Invalid parameters. Expected an object with a "url" property.');
|
|
1421
|
+
}
|
|
1422
|
+
let url = params.url;
|
|
1423
|
+
let newWindow = params.newWindow;
|
|
1424
|
+
if (context.ekoConfig.workingWindowId) {
|
|
1425
|
+
newWindow = false;
|
|
1426
|
+
}
|
|
1427
|
+
else if (!newWindow && !context.variables.get('windowId') && !context.variables.get('tabId')) {
|
|
1428
|
+
// First mandatory opening of a new window
|
|
1429
|
+
newWindow = true;
|
|
1430
|
+
}
|
|
1431
|
+
let tab;
|
|
1432
|
+
if (newWindow) {
|
|
1433
|
+
tab = await open_new_tab(url, true);
|
|
1434
|
+
(_c = (_b = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks) === null || _b === undefined ? undefined : _b.onTabCreated) === null || _c === undefined ? undefined : _c.call(_b, tab.id);
|
|
1435
|
+
}
|
|
1436
|
+
else {
|
|
1437
|
+
let windowId = context.ekoConfig.workingWindowId ? context.ekoConfig.workingWindowId : await getWindowId(context);
|
|
1438
|
+
tab = await open_new_tab(url, false, windowId);
|
|
1439
|
+
(_f = (_e = (_d = context.callback) === null || _d === undefined ? undefined : _d.hooks) === null || _e === undefined ? undefined : _e.onTabCreated) === null || _f === undefined ? undefined : _f.call(_e, tab.id);
|
|
1440
|
+
}
|
|
1441
|
+
let windowId = tab.windowId;
|
|
1442
|
+
let tabId = tab.id;
|
|
1443
|
+
context.variables.set('windowId', windowId);
|
|
1444
|
+
context.variables.set('tabId', tabId);
|
|
1445
|
+
if (newWindow) {
|
|
1446
|
+
let windowIds = context.variables.get('windowIds');
|
|
1447
|
+
if (windowIds) {
|
|
1448
|
+
windowIds.push(windowId);
|
|
1449
|
+
}
|
|
1450
|
+
else {
|
|
1451
|
+
context.variables.set('windowIds', [windowId]);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
return {
|
|
1455
|
+
tabId,
|
|
1456
|
+
windowId,
|
|
1457
|
+
title: tab.title,
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
/**
|
|
1463
|
+
* Current Page Screenshot
|
|
1464
|
+
*/
|
|
1465
|
+
class Screenshot {
|
|
1466
|
+
constructor() {
|
|
1467
|
+
this.name = 'screenshot';
|
|
1468
|
+
this.description = 'Screenshot the current webpage window';
|
|
1469
|
+
this.input_schema = {
|
|
1470
|
+
type: 'object',
|
|
1471
|
+
properties: {},
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Current Page Screenshot
|
|
1476
|
+
*
|
|
1477
|
+
* @param {*} params {}
|
|
1478
|
+
* @returns > { image: { type: 'base64', media_type: 'image/png', data } }
|
|
1479
|
+
*/
|
|
1480
|
+
async execute(context, params) {
|
|
1481
|
+
let windowId = await getWindowId(context);
|
|
1482
|
+
return await screenshot(windowId);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* Browser tab management
|
|
1488
|
+
*/
|
|
1489
|
+
class TabManagement {
|
|
1490
|
+
constructor() {
|
|
1491
|
+
this.name = 'tab_management';
|
|
1492
|
+
this.description = 'Browser tab management, view and operate tabs';
|
|
1493
|
+
this.input_schema = {
|
|
1494
|
+
type: 'object',
|
|
1495
|
+
properties: {
|
|
1496
|
+
command: {
|
|
1497
|
+
type: 'string',
|
|
1498
|
+
description: `The command to perform. The available commands are:
|
|
1499
|
+
* \`tab_all\`: View all tabs and return the tabId and title.
|
|
1500
|
+
* \`current_tab\`: Get current tab information (tabId, url, title).
|
|
1501
|
+
* \`go_back\`: Go back to the previous page in the current tab.
|
|
1502
|
+
* \`change_url [url]\`: open URL in the current tab, eg: \`change_url https://www.google.com\`.
|
|
1503
|
+
* \`close_tab\`: Close the current tab.
|
|
1504
|
+
* \`switch_tab [tabId]\`: Switch to the specified tab using tabId, eg: \`switch_tab 1000\`.
|
|
1505
|
+
* \`new_tab [url]\`: Open a new tab window and open the URL, eg: \`new_tab https://www.google.com\``,
|
|
1506
|
+
},
|
|
1507
|
+
},
|
|
1508
|
+
required: ['command'],
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Tab management
|
|
1513
|
+
*
|
|
1514
|
+
* @param {*} params { command: `new_tab [url]` | 'tab_all' | 'current_tab' | 'go_back' | 'close_tab' | 'switch_tab [tabId]' | `change_url [url]` }
|
|
1515
|
+
* @returns > { result, success: true }
|
|
1516
|
+
*/
|
|
1517
|
+
async execute(context, params) {
|
|
1518
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1519
|
+
if (params === null || !params.command) {
|
|
1520
|
+
throw new Error('Invalid parameters. Expected an object with a "command" property.');
|
|
1521
|
+
}
|
|
1522
|
+
let windowId = await getWindowId(context);
|
|
1523
|
+
let command = params.command.trim();
|
|
1524
|
+
if (command.startsWith('`')) {
|
|
1525
|
+
command = command.substring(1);
|
|
1526
|
+
}
|
|
1527
|
+
if (command.endsWith('`')) {
|
|
1528
|
+
command = command.substring(0, command.length - 1);
|
|
1529
|
+
}
|
|
1530
|
+
let result;
|
|
1531
|
+
if (command == 'tab_all') {
|
|
1532
|
+
result = [];
|
|
1533
|
+
let tabs = await chrome.tabs.query({ windowId: windowId });
|
|
1534
|
+
for (let i = 0; i < tabs.length; i++) {
|
|
1535
|
+
let tab = tabs[i];
|
|
1536
|
+
let tabInfo = {
|
|
1537
|
+
tabId: tab.id,
|
|
1538
|
+
windowId: tab.windowId,
|
|
1539
|
+
title: tab.title,
|
|
1540
|
+
url: tab.url,
|
|
1541
|
+
};
|
|
1542
|
+
if (tab.active) {
|
|
1543
|
+
tabInfo.active = true;
|
|
1544
|
+
}
|
|
1545
|
+
result.push(tabInfo);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
else if (command == 'current_tab') {
|
|
1549
|
+
let tabId = await getTabId(context);
|
|
1550
|
+
let tab = await chrome.tabs.get(tabId);
|
|
1551
|
+
let tabInfo = { tabId, windowId: tab.windowId, title: tab.title, url: tab.url };
|
|
1552
|
+
result = tabInfo;
|
|
1553
|
+
}
|
|
1554
|
+
else if (command == 'go_back') {
|
|
1555
|
+
let tabId = await getTabId(context);
|
|
1556
|
+
await chrome.tabs.goBack(tabId);
|
|
1557
|
+
let tab = await chrome.tabs.get(tabId);
|
|
1558
|
+
let tabInfo = { tabId, windowId: tab.windowId, title: tab.title, url: tab.url };
|
|
1559
|
+
result = tabInfo;
|
|
1560
|
+
}
|
|
1561
|
+
else if (command == 'close_tab') {
|
|
1562
|
+
let closedTabId = await getTabId(context);
|
|
1563
|
+
await chrome.tabs.remove(closedTabId);
|
|
1564
|
+
await sleep(100);
|
|
1565
|
+
let tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
1566
|
+
if (tabs.length == 0) {
|
|
1567
|
+
tabs = await chrome.tabs.query({ status: 'complete', currentWindow: true });
|
|
1568
|
+
}
|
|
1569
|
+
let tab = tabs[tabs.length - 1];
|
|
1570
|
+
if (!tab.active) {
|
|
1571
|
+
await chrome.tabs.update(tab.id, { active: true });
|
|
1572
|
+
}
|
|
1573
|
+
let newTabId = tab.id;
|
|
1574
|
+
context.variables.set('tabId', tab.id);
|
|
1575
|
+
context.variables.set('windowId', tab.windowId);
|
|
1576
|
+
let closeTabInfo = { closedTabId, newTabId, newTabTitle: tab.title };
|
|
1577
|
+
result = closeTabInfo;
|
|
1578
|
+
}
|
|
1579
|
+
else if (command.startsWith('switch_tab')) {
|
|
1580
|
+
let tabId = parseInt(command.replace('switch_tab', '').replace('[', '').replace(']', ''));
|
|
1581
|
+
let tab = await chrome.tabs.update(tabId, { active: true });
|
|
1582
|
+
context.variables.set('tabId', tab.id);
|
|
1583
|
+
context.variables.set('windowId', tab.windowId);
|
|
1584
|
+
let tabInfo = { tabId, windowId: tab.windowId, title: tab.title, url: tab.url };
|
|
1585
|
+
result = tabInfo;
|
|
1586
|
+
}
|
|
1587
|
+
else if (command.startsWith('change_url')) {
|
|
1588
|
+
let url = command.substring('change_url'.length).replace('[', '').replace(']', '').trim();
|
|
1589
|
+
let tabId = await getTabId(context);
|
|
1590
|
+
// await chrome.tabs.update(tabId, { url: url });
|
|
1591
|
+
await executeScript(tabId, () => {
|
|
1592
|
+
location.href = url;
|
|
1593
|
+
}, []);
|
|
1594
|
+
let tab = await waitForTabComplete(tabId);
|
|
1595
|
+
let tabInfo = { tabId, windowId: tab.windowId, title: tab.title, url: tab.url };
|
|
1596
|
+
result = tabInfo;
|
|
1597
|
+
}
|
|
1598
|
+
else if (command.startsWith('new_tab')) {
|
|
1599
|
+
let url = command.replace('new_tab', '').replace('[', '').replace(']', '').replace(/"/g, '');
|
|
1600
|
+
// First mandatory opening of a new window
|
|
1601
|
+
let newWindow = !context.variables.get('windowId') && !context.variables.get('tabId');
|
|
1602
|
+
let tab;
|
|
1603
|
+
if (newWindow) {
|
|
1604
|
+
tab = await open_new_tab(url, true);
|
|
1605
|
+
(_c = (_b = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks) === null || _b === undefined ? undefined : _b.onTabCreated) === null || _c === undefined ? undefined : _c.call(_b, tab.id);
|
|
1606
|
+
}
|
|
1607
|
+
else {
|
|
1608
|
+
let windowId = await getWindowId(context);
|
|
1609
|
+
tab = await open_new_tab(url, false, windowId);
|
|
1610
|
+
(_f = (_e = (_d = context.callback) === null || _d === undefined ? undefined : _d.hooks) === null || _e === undefined ? undefined : _e.onTabCreated) === null || _f === undefined ? undefined : _f.call(_e, tab.id);
|
|
1611
|
+
}
|
|
1612
|
+
let windowId = tab.windowId;
|
|
1613
|
+
let tabId = tab.id;
|
|
1614
|
+
context.variables.set('windowId', windowId);
|
|
1615
|
+
context.variables.set('tabId', tabId);
|
|
1616
|
+
if (newWindow) {
|
|
1617
|
+
let windowIds = context.variables.get('windowIds');
|
|
1618
|
+
if (windowIds) {
|
|
1619
|
+
windowIds.push(windowId);
|
|
1620
|
+
}
|
|
1621
|
+
else {
|
|
1622
|
+
context.variables.set('windowIds', [windowId]);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
let tabInfo = {
|
|
1626
|
+
tabId: tab.id,
|
|
1627
|
+
windowId: tab.windowId,
|
|
1628
|
+
title: tab.title,
|
|
1629
|
+
url: tab.url,
|
|
1630
|
+
};
|
|
1631
|
+
result = tabInfo;
|
|
1632
|
+
}
|
|
1633
|
+
else {
|
|
1634
|
+
throw Error('Unknown command: ' + command);
|
|
1635
|
+
}
|
|
1636
|
+
return result;
|
|
1637
|
+
}
|
|
1638
|
+
destroy(context) {
|
|
1639
|
+
let windowIds = context.variables.get('windowIds');
|
|
1640
|
+
if (windowIds) {
|
|
1641
|
+
for (let i = 0; i < windowIds.length; i++) {
|
|
1642
|
+
chrome.windows.remove(windowIds[i]);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Web Search
|
|
1650
|
+
*/
|
|
1651
|
+
class WebSearch {
|
|
1652
|
+
constructor() {
|
|
1653
|
+
this.name = 'web_search';
|
|
1654
|
+
this.description = 'Search the web based on keywords and return relevant extracted content from webpages.';
|
|
1655
|
+
this.input_schema = {
|
|
1656
|
+
type: 'object',
|
|
1657
|
+
properties: {
|
|
1658
|
+
query: {
|
|
1659
|
+
type: 'string',
|
|
1660
|
+
description: 'search for keywords',
|
|
1661
|
+
},
|
|
1662
|
+
maxResults: {
|
|
1663
|
+
type: 'integer',
|
|
1664
|
+
description: 'Maximum search results, default 5',
|
|
1665
|
+
},
|
|
1666
|
+
},
|
|
1667
|
+
required: ['query'],
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* search
|
|
1672
|
+
*
|
|
1673
|
+
* @param {*} params { url: 'https://www.google.com', query: 'ai agent', maxResults: 5 }
|
|
1674
|
+
* @returns > [{ title, url, content }]
|
|
1675
|
+
*/
|
|
1676
|
+
async execute(context, params) {
|
|
1677
|
+
var _a;
|
|
1678
|
+
if (typeof params !== 'object' || params === null || !params.query) {
|
|
1679
|
+
throw new Error('Invalid parameters. Expected an object with a "query" property.');
|
|
1680
|
+
}
|
|
1681
|
+
let url = params.url;
|
|
1682
|
+
let query = params.query;
|
|
1683
|
+
let maxResults = params.maxResults;
|
|
1684
|
+
if (!url) {
|
|
1685
|
+
url = 'https://www.google.com';
|
|
1686
|
+
}
|
|
1687
|
+
let taskId = new Date().getTime() + '';
|
|
1688
|
+
let searchs = [{ url: url, keyword: query }];
|
|
1689
|
+
let searchInfo = await deepSearch(context, taskId, searchs, maxResults || 5, context.ekoConfig.workingWindowId);
|
|
1690
|
+
let links = ((_a = searchInfo.result[0]) === null || _a === undefined ? undefined : _a.links) || [];
|
|
1691
|
+
return links.filter((s) => s.content);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
const deepSearchInjects = {
|
|
1695
|
+
'bing.com': {
|
|
1696
|
+
filename: 'bing.js',
|
|
1697
|
+
buildSearchUrl: function (url, keyword) {
|
|
1698
|
+
return 'https://bing.com/search?q=' + encodeURI(keyword);
|
|
1699
|
+
},
|
|
1700
|
+
},
|
|
1701
|
+
'duckduckgo.com': {
|
|
1702
|
+
filename: 'duckduckgo.js',
|
|
1703
|
+
buildSearchUrl: function (url, keyword) {
|
|
1704
|
+
return 'https://duckduckgo.com/?q=' + encodeURI(keyword);
|
|
1705
|
+
},
|
|
1706
|
+
},
|
|
1707
|
+
'google.com': {
|
|
1708
|
+
filename: 'google.js',
|
|
1709
|
+
buildSearchUrl: function (url, keyword) {
|
|
1710
|
+
return 'https://www.google.com/search?q=' + encodeURI(keyword);
|
|
1711
|
+
},
|
|
1712
|
+
},
|
|
1713
|
+
default: {
|
|
1714
|
+
filename: 'google.js',
|
|
1715
|
+
buildSearchUrl: function (url, keyword) {
|
|
1716
|
+
url = url.trim();
|
|
1717
|
+
let idx = url.indexOf('//');
|
|
1718
|
+
if (idx > -1) {
|
|
1719
|
+
url = url.substring(idx + 2);
|
|
1720
|
+
}
|
|
1721
|
+
idx = url.indexOf('/', 2);
|
|
1722
|
+
if (idx > -1) {
|
|
1723
|
+
url = url.substring(0, idx);
|
|
1724
|
+
}
|
|
1725
|
+
keyword = 'site:' + url + ' ' + keyword;
|
|
1726
|
+
return 'https://www.google.com/search?q=' + encodeURIComponent(keyword);
|
|
1727
|
+
},
|
|
1728
|
+
},
|
|
1729
|
+
};
|
|
1730
|
+
function buildDeepSearchUrl(url, keyword) {
|
|
1731
|
+
let idx = url.indexOf('/', url.indexOf('//') + 2);
|
|
1732
|
+
let baseUrl = idx > -1 ? url.substring(0, idx) : url;
|
|
1733
|
+
let domains = Object.keys(deepSearchInjects);
|
|
1734
|
+
let inject = null;
|
|
1735
|
+
for (let j = 0; j < domains.length; j++) {
|
|
1736
|
+
let domain = domains[j];
|
|
1737
|
+
if (baseUrl == domain || baseUrl.endsWith('.' + domain) || baseUrl.endsWith('/' + domain)) {
|
|
1738
|
+
inject = deepSearchInjects[domain];
|
|
1739
|
+
break;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
if (!inject) {
|
|
1743
|
+
inject = deepSearchInjects['default'];
|
|
1744
|
+
}
|
|
1745
|
+
return {
|
|
1746
|
+
filename: inject.filename,
|
|
1747
|
+
url: inject.buildSearchUrl(url, keyword),
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
// Event
|
|
1751
|
+
const tabsUpdateEvent = new MsgEvent();
|
|
1752
|
+
chrome.tabs.onUpdated.addListener(async function (tabId, changeInfo, tab) {
|
|
1753
|
+
await tabsUpdateEvent.publish({ tabId, changeInfo, tab });
|
|
1754
|
+
});
|
|
1755
|
+
/**
|
|
1756
|
+
* deep search
|
|
1757
|
+
*
|
|
1758
|
+
* @param {string} taskId task id
|
|
1759
|
+
* @param {array} searchs search list => [{ url: 'https://bing.com', keyword: 'ai' }]
|
|
1760
|
+
* @param {number} detailsMaxNum Maximum crawling quantity per search detail page
|
|
1761
|
+
*/
|
|
1762
|
+
async function deepSearch(context, taskId, searchs, detailsMaxNum, windowId) {
|
|
1763
|
+
let closeWindow = false;
|
|
1764
|
+
if (!windowId) {
|
|
1765
|
+
// open new window
|
|
1766
|
+
let window = await chrome.windows.create({
|
|
1767
|
+
type: 'normal',
|
|
1768
|
+
state: 'maximized',
|
|
1769
|
+
url: null,
|
|
1770
|
+
});
|
|
1771
|
+
windowId = window.id;
|
|
1772
|
+
closeWindow = true;
|
|
1773
|
+
}
|
|
1774
|
+
windowId = windowId;
|
|
1775
|
+
// crawler the search page details page link
|
|
1776
|
+
// [{ links: [{ title, url }] }]
|
|
1777
|
+
let detailLinkGroups = await doDetailLinkGroups(context, taskId, searchs, detailsMaxNum, windowId);
|
|
1778
|
+
// crawler all details page content and comments
|
|
1779
|
+
let searchInfo = await doPageContent(context, taskId, detailLinkGroups, windowId);
|
|
1780
|
+
console.log('searchInfo: ', searchInfo);
|
|
1781
|
+
// close window
|
|
1782
|
+
closeWindow && chrome.windows.remove(windowId);
|
|
1783
|
+
return searchInfo;
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* crawler the search page details page link
|
|
1787
|
+
*
|
|
1788
|
+
* @param {string} taskId task id
|
|
1789
|
+
* @param {array} searchs search list => [{ url: 'https://bing.com', keyword: 'ai' }]
|
|
1790
|
+
* @param {number} detailsMaxNum Maximum crawling quantity per search detail page
|
|
1791
|
+
* @param {*} window
|
|
1792
|
+
* @returns [{ links: [{ title, url }] }]
|
|
1793
|
+
*/
|
|
1794
|
+
async function doDetailLinkGroups(context, taskId, searchs, detailsMaxNum, windowId) {
|
|
1795
|
+
var _a, _b, _c;
|
|
1796
|
+
let detailLinkGroups = [];
|
|
1797
|
+
let countDownLatch = new CountDownLatch(searchs.length);
|
|
1798
|
+
for (let i = 0; i < searchs.length; i++) {
|
|
1799
|
+
try {
|
|
1800
|
+
// script name & build search URL
|
|
1801
|
+
const { filename, url } = buildDeepSearchUrl(searchs[i].url, searchs[i].keyword);
|
|
1802
|
+
// open new Tab
|
|
1803
|
+
let tab = await chrome.tabs.create({
|
|
1804
|
+
url: url,
|
|
1805
|
+
windowId,
|
|
1806
|
+
});
|
|
1807
|
+
(_c = (_b = (_a = context.callback) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.onTabCreated) === null || _c === void 0 ? void 0 : _c.call(_b, tab.id);
|
|
1808
|
+
let eventId = taskId + '_' + i;
|
|
1809
|
+
// monitor Tab status
|
|
1810
|
+
tabsUpdateEvent.addListener(async function (obj) {
|
|
1811
|
+
if (obj.tabId != tab.id) {
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
if (obj.changeInfo.status === 'complete') {
|
|
1815
|
+
tabsUpdateEvent.removeListener(eventId);
|
|
1816
|
+
// inject js
|
|
1817
|
+
await injectScript(tab.id, filename);
|
|
1818
|
+
await sleep(1000);
|
|
1819
|
+
// crawler the search page details page
|
|
1820
|
+
// { links: [{ title, url }] }
|
|
1821
|
+
let detailLinks = await chrome.tabs.sendMessage(tab.id, {
|
|
1822
|
+
type: 'page:getDetailLinks',
|
|
1823
|
+
keyword: searchs[i].keyword,
|
|
1824
|
+
});
|
|
1825
|
+
if (!detailLinks || !detailLinks.links) {
|
|
1826
|
+
// TODO error
|
|
1827
|
+
detailLinks = { links: [] };
|
|
1828
|
+
}
|
|
1829
|
+
console.log('detailLinks: ', detailLinks);
|
|
1830
|
+
let links = detailLinks.links.slice(0, detailsMaxNum);
|
|
1831
|
+
detailLinkGroups.push({ url, links, filename });
|
|
1832
|
+
countDownLatch.countDown();
|
|
1833
|
+
chrome.tabs.remove(tab.id);
|
|
1834
|
+
}
|
|
1835
|
+
else if (obj.changeInfo.status === 'unloaded') {
|
|
1836
|
+
countDownLatch.countDown();
|
|
1837
|
+
chrome.tabs.remove(tab.id);
|
|
1838
|
+
tabsUpdateEvent.removeListener(eventId);
|
|
1839
|
+
}
|
|
1840
|
+
}, eventId);
|
|
1841
|
+
}
|
|
1842
|
+
catch (e) {
|
|
1843
|
+
console.error(e);
|
|
1844
|
+
countDownLatch.countDown();
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
await countDownLatch.await(30000);
|
|
1848
|
+
return detailLinkGroups;
|
|
1849
|
+
}
|
|
1850
|
+
/**
|
|
1851
|
+
* page content
|
|
1852
|
+
*
|
|
1853
|
+
* @param {string} taskId task id
|
|
1854
|
+
* @param {array} detailLinkGroups details page group
|
|
1855
|
+
* @param {*} window
|
|
1856
|
+
* @returns search info
|
|
1857
|
+
*/
|
|
1858
|
+
async function doPageContent(context, taskId, detailLinkGroups, windowId) {
|
|
1859
|
+
var _a, _b, _c;
|
|
1860
|
+
const searchInfo = {
|
|
1861
|
+
total: 0,
|
|
1862
|
+
running: 0,
|
|
1863
|
+
succeed: 0,
|
|
1864
|
+
failed: 0,
|
|
1865
|
+
failedLinks: [],
|
|
1866
|
+
result: detailLinkGroups,
|
|
1867
|
+
};
|
|
1868
|
+
for (let i = 0; i < detailLinkGroups.length; i++) {
|
|
1869
|
+
let links = detailLinkGroups[i].links;
|
|
1870
|
+
searchInfo.total += links.length;
|
|
1871
|
+
}
|
|
1872
|
+
let countDownLatch = new CountDownLatch(searchInfo.total);
|
|
1873
|
+
for (let i = 0; i < detailLinkGroups.length; i++) {
|
|
1874
|
+
let filename = detailLinkGroups[i].filename;
|
|
1875
|
+
let links = detailLinkGroups[i].links;
|
|
1876
|
+
for (let j = 0; j < links.length; j++) {
|
|
1877
|
+
let link = links[j];
|
|
1878
|
+
// open new tab
|
|
1879
|
+
let tab = await chrome.tabs.create({
|
|
1880
|
+
url: link.url,
|
|
1881
|
+
windowId,
|
|
1882
|
+
});
|
|
1883
|
+
(_c = (_b = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks) === null || _b === undefined ? undefined : _b.onTabCreated) === null || _c === undefined ? undefined : _c.call(_b, tab.id);
|
|
1884
|
+
searchInfo.running++;
|
|
1885
|
+
let eventId = taskId + '_' + i + '_' + j;
|
|
1886
|
+
// Create a timeout promise
|
|
1887
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1888
|
+
setTimeout(() => reject(new Error('Page load timeout')), 10000); // Timeout after 10 seconds
|
|
1889
|
+
});
|
|
1890
|
+
// Create a tab monitoring promise
|
|
1891
|
+
const monitorTabPromise = new Promise(async (resolve, reject) => {
|
|
1892
|
+
tabsUpdateEvent.addListener(async function onTabUpdated(obj) {
|
|
1893
|
+
if (obj.tabId !== tab.id)
|
|
1894
|
+
return;
|
|
1895
|
+
if (obj.changeInfo.status === 'complete') {
|
|
1896
|
+
tabsUpdateEvent.removeListener(eventId);
|
|
1897
|
+
try {
|
|
1898
|
+
// Inject script and get page content
|
|
1899
|
+
await injectScript(tab.id, filename);
|
|
1900
|
+
await sleep(1000);
|
|
1901
|
+
let result = await chrome.tabs.sendMessage(tab.id, {
|
|
1902
|
+
type: 'page:getContent',
|
|
1903
|
+
});
|
|
1904
|
+
if (!result)
|
|
1905
|
+
throw new Error('No Result');
|
|
1906
|
+
link.content = result.content;
|
|
1907
|
+
link.page_title = result.title;
|
|
1908
|
+
searchInfo.succeed++;
|
|
1909
|
+
resolve(); // Resolve the promise if successful
|
|
1910
|
+
}
|
|
1911
|
+
catch (error) {
|
|
1912
|
+
searchInfo.failed++;
|
|
1913
|
+
searchInfo.failedLinks.push(link);
|
|
1914
|
+
reject(error); // Reject the promise on error
|
|
1915
|
+
}
|
|
1916
|
+
finally {
|
|
1917
|
+
searchInfo.running--;
|
|
1918
|
+
countDownLatch.countDown();
|
|
1919
|
+
chrome.tabs.remove(tab.id);
|
|
1920
|
+
tabsUpdateEvent.removeListener(eventId);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
else if (obj.changeInfo.status === 'unloaded') {
|
|
1924
|
+
searchInfo.running--;
|
|
1925
|
+
countDownLatch.countDown();
|
|
1926
|
+
chrome.tabs.remove(tab.id);
|
|
1927
|
+
tabsUpdateEvent.removeListener(eventId);
|
|
1928
|
+
reject(new Error('Tab unloaded')); // Reject if the tab is unloaded
|
|
1929
|
+
}
|
|
1930
|
+
}, eventId);
|
|
1931
|
+
});
|
|
1932
|
+
// Use Promise.race to enforce the timeout
|
|
1933
|
+
try {
|
|
1934
|
+
await Promise.race([monitorTabPromise, timeoutPromise]);
|
|
1935
|
+
}
|
|
1936
|
+
catch (e) {
|
|
1937
|
+
console.error(`${link.title} failed:`, e);
|
|
1938
|
+
searchInfo.running--;
|
|
1939
|
+
searchInfo.failed++;
|
|
1940
|
+
searchInfo.failedLinks.push(link);
|
|
1941
|
+
countDownLatch.countDown();
|
|
1942
|
+
chrome.tabs.remove(tab.id); // Clean up tab on failure
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
await countDownLatch.await(60000);
|
|
1947
|
+
return searchInfo;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
class RequestLogin {
|
|
1951
|
+
constructor() {
|
|
1952
|
+
this.name = 'request_login';
|
|
1953
|
+
this.description =
|
|
1954
|
+
'Login to this website, assist with identity verification when manual intervention is needed, guide users through the login process, and wait for their confirmation of successful login.';
|
|
1955
|
+
this.input_schema = {
|
|
1956
|
+
type: 'object',
|
|
1957
|
+
properties: {},
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
async execute(context, params) {
|
|
1961
|
+
if (!params.force && await this.isLoginIn(context)) {
|
|
1962
|
+
return true;
|
|
1963
|
+
}
|
|
1964
|
+
let tabId = await getTabId(context);
|
|
1965
|
+
let task_id = 'login_required_' + tabId;
|
|
1966
|
+
const request_user_help = async () => {
|
|
1967
|
+
await chrome.tabs.sendMessage(tabId, {
|
|
1968
|
+
type: 'request_user_help',
|
|
1969
|
+
task_id,
|
|
1970
|
+
failure_type: 'login_required',
|
|
1971
|
+
failure_message: 'Access page require user authentication.',
|
|
1972
|
+
});
|
|
1973
|
+
};
|
|
1974
|
+
const login_interval = setInterval(async () => {
|
|
1975
|
+
try {
|
|
1976
|
+
request_user_help();
|
|
1977
|
+
}
|
|
1978
|
+
catch (e) {
|
|
1979
|
+
clearInterval(login_interval);
|
|
1980
|
+
}
|
|
1981
|
+
}, 2000);
|
|
1982
|
+
try {
|
|
1983
|
+
return await this.awaitLogin(tabId, task_id);
|
|
1984
|
+
}
|
|
1985
|
+
finally {
|
|
1986
|
+
clearInterval(login_interval);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
async awaitLogin(tabId, task_id) {
|
|
1990
|
+
return new Promise((resolve) => {
|
|
1991
|
+
const checkTabClosedInterval = setInterval(async () => {
|
|
1992
|
+
const tabExists = await doesTabExists(tabId);
|
|
1993
|
+
if (!tabExists) {
|
|
1994
|
+
clearInterval(checkTabClosedInterval);
|
|
1995
|
+
resolve(false);
|
|
1996
|
+
chrome.runtime.onMessage.removeListener(listener);
|
|
1997
|
+
}
|
|
1998
|
+
}, 1000);
|
|
1999
|
+
const listener = (message) => {
|
|
2000
|
+
if (message.type === 'issue_resolved' && message.task_id === task_id) {
|
|
2001
|
+
resolve(true);
|
|
2002
|
+
clearInterval(checkTabClosedInterval);
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
chrome.runtime.onMessage.addListener(listener);
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
async isLoginIn(context) {
|
|
2009
|
+
let windowId = await getWindowId(context);
|
|
2010
|
+
let screenshot_result = await screenshot(windowId, true);
|
|
2011
|
+
let messages = [
|
|
2012
|
+
{
|
|
2013
|
+
role: 'user',
|
|
2014
|
+
content: [
|
|
2015
|
+
{
|
|
2016
|
+
type: 'image',
|
|
2017
|
+
source: screenshot_result.image,
|
|
2018
|
+
},
|
|
2019
|
+
{
|
|
2020
|
+
type: 'text',
|
|
2021
|
+
text: 'Check if the current website is logged in. If not logged in, output `NOT_LOGIN`. If logged in, output `LOGGED_IN`. Output directly without explanation.',
|
|
2022
|
+
},
|
|
2023
|
+
],
|
|
2024
|
+
},
|
|
2025
|
+
];
|
|
2026
|
+
let response = await context.llmProvider.generateText(messages, { maxTokens: 256 });
|
|
2027
|
+
let text = response.textContent;
|
|
2028
|
+
if (!text) {
|
|
2029
|
+
text = JSON.stringify(response.content);
|
|
2030
|
+
}
|
|
2031
|
+
return text.indexOf('LOGGED_IN') > -1;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
class CancelWorkflow {
|
|
2036
|
+
constructor() {
|
|
2037
|
+
this.name = 'cancel_workflow';
|
|
2038
|
+
this.description = 'Cancel the workflow. If any tool consistently encounters exceptions, invoke this tool to cancel the workflow.';
|
|
2039
|
+
this.input_schema = {
|
|
2040
|
+
type: 'object',
|
|
2041
|
+
properties: {
|
|
2042
|
+
reason: {
|
|
2043
|
+
type: 'string',
|
|
2044
|
+
description: 'Why the workflow should be cancelled.',
|
|
2045
|
+
},
|
|
2046
|
+
},
|
|
2047
|
+
required: ['reason'],
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
async execute(context, params) {
|
|
2051
|
+
var _a;
|
|
2052
|
+
if (typeof params !== 'object' || params === null || !params.reason) {
|
|
2053
|
+
throw new Error('Invalid parameters. Expected an object with a "reason" property.');
|
|
2054
|
+
}
|
|
2055
|
+
const reason = params.reason;
|
|
2056
|
+
console.log("The workflow has been cancelled because: " + reason);
|
|
2057
|
+
await ((_a = context.workflow) === null || _a === undefined ? undefined : _a.cancel());
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
class HumanInputText {
|
|
2063
|
+
constructor() {
|
|
2064
|
+
this.name = 'human_input_text';
|
|
2065
|
+
this.description = 'When you are unsure about the details of your next action, call me and ask the user for details in the "question" field. The user will provide you with a text as an answer.';
|
|
2066
|
+
this.input_schema = {
|
|
2067
|
+
type: 'object',
|
|
2068
|
+
properties: {
|
|
2069
|
+
question: {
|
|
2070
|
+
type: 'string',
|
|
2071
|
+
description: 'Ask the user here.',
|
|
2072
|
+
},
|
|
2073
|
+
},
|
|
2074
|
+
required: ['question'],
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
async execute(context, params) {
|
|
2078
|
+
var _a;
|
|
2079
|
+
if (typeof params !== 'object' || params === null || !params.question) {
|
|
2080
|
+
throw new Error('Invalid parameters. Expected an object with a "question" property.');
|
|
2081
|
+
}
|
|
2082
|
+
const question = params.question;
|
|
2083
|
+
console.log("question: " + question);
|
|
2084
|
+
let onHumanInputText = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks.onHumanInputText;
|
|
2085
|
+
if (onHumanInputText) {
|
|
2086
|
+
let answer;
|
|
2087
|
+
try {
|
|
2088
|
+
answer = await onHumanInputText(question);
|
|
2089
|
+
}
|
|
2090
|
+
catch (e) {
|
|
2091
|
+
console.error(e);
|
|
2092
|
+
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2093
|
+
}
|
|
2094
|
+
console.log("answer: " + answer);
|
|
2095
|
+
return { status: "OK", answer: answer };
|
|
2096
|
+
}
|
|
2097
|
+
else {
|
|
2098
|
+
console.error("`onHumanInputText` not implemented");
|
|
2099
|
+
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
class HumanInputSingleChoice {
|
|
2104
|
+
constructor() {
|
|
2105
|
+
this.name = 'human_input_single_choice';
|
|
2106
|
+
this.description = 'When you are unsure about the details of your next action, call me and ask the user for details in the "question" field with at least 2 choices. The user will provide you with ONE choice as an answer.';
|
|
2107
|
+
this.input_schema = {
|
|
2108
|
+
type: 'object',
|
|
2109
|
+
properties: {
|
|
2110
|
+
question: {
|
|
2111
|
+
type: 'string',
|
|
2112
|
+
description: 'Ask the user here.',
|
|
2113
|
+
},
|
|
2114
|
+
choices: {
|
|
2115
|
+
type: 'array',
|
|
2116
|
+
description: 'All of the choices.',
|
|
2117
|
+
}
|
|
2118
|
+
},
|
|
2119
|
+
required: ['question', 'choices'],
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
async execute(context, params) {
|
|
2123
|
+
var _a;
|
|
2124
|
+
if (typeof params !== 'object' || params === null || !params.question || !params.choices) {
|
|
2125
|
+
throw new Error('Invalid parameters. Expected an object with a "question" and "choices" property.');
|
|
2126
|
+
}
|
|
2127
|
+
const question = params.question;
|
|
2128
|
+
const choices = params.choices;
|
|
2129
|
+
console.log("question: " + question);
|
|
2130
|
+
console.log("choices: " + choices);
|
|
2131
|
+
let onHumanInputSingleChoice = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks.onHumanInputSingleChoice;
|
|
2132
|
+
if (onHumanInputSingleChoice) {
|
|
2133
|
+
let answer;
|
|
2134
|
+
try {
|
|
2135
|
+
answer = await onHumanInputSingleChoice(question, choices);
|
|
2136
|
+
}
|
|
2137
|
+
catch (e) {
|
|
2138
|
+
console.error(e);
|
|
2139
|
+
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2140
|
+
}
|
|
2141
|
+
console.log("answer: " + answer);
|
|
2142
|
+
return { status: "OK", answer: answer };
|
|
2143
|
+
}
|
|
2144
|
+
else {
|
|
2145
|
+
console.error("`onHumanInputSingleChoice` not implemented");
|
|
2146
|
+
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
class HumanInputMultipleChoice {
|
|
2151
|
+
constructor() {
|
|
2152
|
+
this.name = 'human_input_multiple_choice';
|
|
2153
|
+
this.description = 'When you are unsure about the details of your next action, call me and ask the user for details in the "question" field with at least 2 choices. The user will provide you with ONE or MORE choice as an answer.';
|
|
2154
|
+
this.input_schema = {
|
|
2155
|
+
type: 'object',
|
|
2156
|
+
properties: {
|
|
2157
|
+
question: {
|
|
2158
|
+
type: 'string',
|
|
2159
|
+
description: 'Ask the user here.',
|
|
2160
|
+
},
|
|
2161
|
+
choices: {
|
|
2162
|
+
type: 'array',
|
|
2163
|
+
description: 'All of the choices.',
|
|
2164
|
+
}
|
|
2165
|
+
},
|
|
2166
|
+
required: ['question', 'choices'],
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
async execute(context, params) {
|
|
2170
|
+
var _a;
|
|
2171
|
+
if (typeof params !== 'object' || params === null || !params.question || !params.choices) {
|
|
2172
|
+
throw new Error('Invalid parameters. Expected an object with a "question" and "choices" property.');
|
|
2173
|
+
}
|
|
2174
|
+
const question = params.question;
|
|
2175
|
+
const choices = params.choices;
|
|
2176
|
+
console.log("question: " + question);
|
|
2177
|
+
console.log("choices: " + choices);
|
|
2178
|
+
let onHumanInputMultipleChoice = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks.onHumanInputMultipleChoice;
|
|
2179
|
+
if (onHumanInputMultipleChoice) {
|
|
2180
|
+
let answer;
|
|
2181
|
+
try {
|
|
2182
|
+
answer = await onHumanInputMultipleChoice(question, choices);
|
|
2183
|
+
}
|
|
2184
|
+
catch (e) {
|
|
2185
|
+
console.error(e);
|
|
2186
|
+
return { status: "`onHumanInputMultipleChoice` not implemented", answer: [] };
|
|
2187
|
+
}
|
|
2188
|
+
console.log("answer: " + answer);
|
|
2189
|
+
return { status: "OK", answer: answer };
|
|
2190
|
+
}
|
|
2191
|
+
else {
|
|
2192
|
+
console.error("Cannot get user's answer.");
|
|
2193
|
+
return { status: "Error: Cannot get user's answer.", answer: [] };
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
class HumanOperate {
|
|
2198
|
+
constructor() {
|
|
2199
|
+
this.name = 'human_operate';
|
|
2200
|
+
this.description = 'When you encounter operations that require login, CAPTCHA verification, or other tasks that you cannot complete, please call this tool, transfer control to the user, and explain why.';
|
|
2201
|
+
this.input_schema = {
|
|
2202
|
+
type: 'object',
|
|
2203
|
+
properties: {
|
|
2204
|
+
reason: {
|
|
2205
|
+
type: 'string',
|
|
2206
|
+
description: 'The reason why you need to transfer control.',
|
|
2207
|
+
},
|
|
2208
|
+
},
|
|
2209
|
+
required: ['reason'],
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
async execute(context, params) {
|
|
2213
|
+
var _a;
|
|
2214
|
+
if (typeof params !== 'object' || params === null || !params.reason) {
|
|
2215
|
+
throw new Error('Invalid parameters. Expected an object with a "reason" property.');
|
|
2216
|
+
}
|
|
2217
|
+
const reason = params.reason;
|
|
2218
|
+
console.log("reason: " + reason);
|
|
2219
|
+
let onHumanOperate = (_a = context.callback) === null || _a === undefined ? undefined : _a.hooks.onHumanOperate;
|
|
2220
|
+
if (onHumanOperate) {
|
|
2221
|
+
let userOperation;
|
|
2222
|
+
try {
|
|
2223
|
+
userOperation = await onHumanOperate(reason);
|
|
2224
|
+
}
|
|
2225
|
+
catch (e) {
|
|
2226
|
+
console.error(e);
|
|
2227
|
+
return { status: "`onHumanOperate` not implemented", userOperation: "" };
|
|
2228
|
+
}
|
|
2229
|
+
console.log("userOperation: " + userOperation);
|
|
2230
|
+
return { status: "OK", userOperation: userOperation };
|
|
2231
|
+
}
|
|
2232
|
+
else {
|
|
2233
|
+
console.error("Cannot get user's operation.");
|
|
2234
|
+
return { status: "Error: Cannot get user's operation.", userOperation: "" };
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
class SummaryWorkflow {
|
|
2240
|
+
constructor() {
|
|
2241
|
+
this.name = 'summary_workflow';
|
|
2242
|
+
this.description = 'Summarize briefly what this workflow has accomplished.';
|
|
2243
|
+
this.input_schema = {
|
|
2244
|
+
type: 'object',
|
|
2245
|
+
properties: {
|
|
2246
|
+
summary: {
|
|
2247
|
+
type: 'string',
|
|
2248
|
+
description: 'Your summary in markdown format.',
|
|
2249
|
+
},
|
|
2250
|
+
},
|
|
2251
|
+
required: ['summary'],
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
async execute(context, params) {
|
|
2255
|
+
var _a, _b, _c;
|
|
2256
|
+
if (typeof params !== 'object' || params === null || !params.summary) {
|
|
2257
|
+
throw new Error('Invalid parameters. Expected an object with a "summary" property.');
|
|
2258
|
+
}
|
|
2259
|
+
const summary = params.summary;
|
|
2260
|
+
console.log("summary: " + summary);
|
|
2261
|
+
await ((_c = (_a = context.callback) === null || _a === undefined ? undefined : (_b = _a.hooks).onSummaryWorkflow) === null || _c === undefined ? undefined : _c.call(_b, summary));
|
|
2262
|
+
return { status: "OK" };
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
var tools = /*#__PURE__*/Object.freeze({
|
|
2267
|
+
__proto__: null,
|
|
2268
|
+
BrowserUse: BrowserUse,
|
|
2269
|
+
CancelWorkflow: CancelWorkflow,
|
|
2270
|
+
ElementClick: ElementClick,
|
|
2271
|
+
ExportFile: ExportFile,
|
|
2272
|
+
ExtractContent: ExtractContent,
|
|
2273
|
+
FindElementPosition: FindElementPosition,
|
|
2274
|
+
GetAllTabs: GetAllTabs,
|
|
2275
|
+
HumanInputMultipleChoice: HumanInputMultipleChoice,
|
|
2276
|
+
HumanInputSingleChoice: HumanInputSingleChoice,
|
|
2277
|
+
HumanInputText: HumanInputText,
|
|
2278
|
+
HumanOperate: HumanOperate,
|
|
2279
|
+
OpenUrl: OpenUrl,
|
|
2280
|
+
RequestLogin: RequestLogin,
|
|
2281
|
+
Screenshot: Screenshot,
|
|
2282
|
+
SummaryWorkflow: SummaryWorkflow,
|
|
2283
|
+
TabManagement: TabManagement,
|
|
2284
|
+
WebSearch: WebSearch
|
|
2285
|
+
});
|
|
2286
|
+
|
|
2287
|
+
async function pub(tabId, event, params) {
|
|
2288
|
+
return await chrome.tabs.sendMessage(tabId, {
|
|
2289
|
+
type: 'eko:message',
|
|
2290
|
+
event,
|
|
2291
|
+
params,
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
async function getLLMConfig(name = 'llmConfig') {
|
|
2295
|
+
let result = await chrome.storage.sync.get([name]);
|
|
2296
|
+
return result[name];
|
|
2297
|
+
}
|
|
2298
|
+
function loadTools() {
|
|
2299
|
+
let toolsMap = new Map();
|
|
2300
|
+
for (const key in tools) {
|
|
2301
|
+
let tool = tools[key];
|
|
2302
|
+
if (typeof tool === 'function' && tool.prototype && 'execute' in tool.prototype) {
|
|
2303
|
+
try {
|
|
2304
|
+
let instance = new tool();
|
|
2305
|
+
toolsMap.set(instance.name || key, instance);
|
|
2306
|
+
}
|
|
2307
|
+
catch (e) {
|
|
2308
|
+
console.error(`Failed to instantiate ${key}:`, e);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
return toolsMap;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
export { browser, getLLMConfig, loadTools, pub, tools, utils };
|