@eko-ai/eko 1.3.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -19
- package/dist/agent/a2a.d.ts +7 -0
- package/dist/agent/a2a.d.ts.map +1 -0
- package/dist/agent/base.d.ts +47 -0
- package/dist/agent/base.d.ts.map +1 -0
- package/dist/agent/browser/browser_base.d.ts +30 -0
- package/dist/agent/browser/browser_base.d.ts.map +1 -0
- package/dist/agent/browser/browser_labels.d.ts +21 -0
- package/dist/agent/browser/browser_labels.d.ts.map +1 -0
- package/dist/agent/browser/browser_screen.d.ts +16 -0
- package/dist/agent/browser/browser_screen.d.ts.map +1 -0
- package/dist/agent/browser/build_dom_tree.d.ts +2 -0
- package/dist/agent/browser/build_dom_tree.d.ts.map +1 -0
- package/dist/agent/browser/index.d.ts +6 -0
- package/dist/agent/browser/index.d.ts.map +1 -0
- package/dist/agent/chat.d.ts +7 -0
- package/dist/agent/chat.d.ts.map +1 -0
- package/dist/agent/computer.d.ts +23 -0
- package/dist/agent/computer.d.ts.map +1 -0
- package/dist/agent/file.d.ts +14 -0
- package/dist/agent/file.d.ts.map +1 -0
- package/dist/agent/index.d.ts +9 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/shell.d.ts +14 -0
- package/dist/agent/shell.d.ts.map +1 -0
- package/dist/agent/timer.d.ts +5 -0
- package/dist/agent/timer.d.ts.map +1 -0
- package/dist/common/log.d.ts +43 -2
- package/dist/common/log.d.ts.map +1 -0
- package/dist/common/utils.d.ts +12 -0
- package/dist/common/utils.d.ts.map +1 -0
- package/dist/common/xml.d.ts +7 -0
- package/dist/common/xml.d.ts.map +1 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/core/chain.d.ts +45 -0
- package/dist/core/chain.d.ts.map +1 -0
- package/dist/core/context.d.ts +23 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/index.d.ts +18 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/plan.d.ts +11 -0
- package/dist/core/plan.d.ts.map +1 -0
- package/dist/index.cjs.js +18163 -10855
- package/dist/index.d.ts +13 -10
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +18147 -10887
- package/dist/llm/index.d.ts +15 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/mcp/client.d.ts +28 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +28 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/prompt/agent.d.ts +6 -0
- package/dist/prompt/agent.d.ts.map +1 -0
- package/dist/prompt/plan.d.ts +4 -0
- package/dist/prompt/plan.d.ts.map +1 -0
- package/dist/tools/foreach_task.d.ts +12 -0
- package/dist/tools/foreach_task.d.ts.map +1 -0
- package/dist/tools/human_interact.d.ts +12 -0
- package/dist/tools/human_interact.d.ts.map +1 -0
- package/dist/tools/index.d.ts +19 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/task_node_status.d.ts +12 -0
- package/dist/tools/task_node_status.d.ts.map +1 -0
- package/dist/tools/variable_storage.d.ts +12 -0
- package/dist/tools/variable_storage.d.ts.map +1 -0
- package/dist/tools/watch_trigger.d.ts +12 -0
- package/dist/tools/watch_trigger.d.ts.map +1 -0
- package/dist/tools/wrapper.d.ts +12 -0
- package/dist/tools/wrapper.d.ts.map +1 -0
- package/dist/types/core.types.d.ts +114 -0
- package/dist/types/core.types.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/llm.types.d.ts +80 -51
- package/dist/types/llm.types.d.ts.map +1 -0
- package/dist/types/mcp.types.d.ts +35 -0
- package/dist/types/mcp.types.d.ts.map +1 -0
- package/dist/types/tools.types.d.ts +56 -152
- package/dist/types/tools.types.d.ts.map +1 -0
- package/package.json +18 -58
- package/dist/common/chrome/proxy.d.ts +0 -24
- package/dist/common/context-compressor.d.ts +0 -10
- package/dist/common/summarize-workflow.d.ts +0 -2
- package/dist/common/tools/cancel_workflow.d.ts +0 -9
- package/dist/common/tools/document_agent.d.ts +0 -8
- package/dist/common/tools/human.d.ts +0 -30
- package/dist/common/tools/index.d.ts +0 -4
- package/dist/common/tools/write_context.d.ts +0 -7
- package/dist/core/eko.d.ts +0 -30
- package/dist/core/tool-registry.d.ts +0 -13
- package/dist/extension/content/index.d.ts +0 -1
- package/dist/extension/core.d.ts +0 -11
- package/dist/extension/index.d.ts +0 -7
- package/dist/extension/script/bing.js +0 -25
- package/dist/extension/script/build_dom_tree.d.ts +0 -38
- package/dist/extension/script/build_dom_tree.js +0 -662
- package/dist/extension/script/common.js +0 -212
- package/dist/extension/script/duckduckgo.js +0 -25
- package/dist/extension/script/google.js +0 -26
- package/dist/extension/tools/browser.d.ts +0 -23
- package/dist/extension/tools/browser_action.d.ts +0 -20
- package/dist/extension/tools/export_file.d.ts +0 -18
- package/dist/extension/tools/extract_content.d.ts +0 -18
- package/dist/extension/tools/get_all_tabs.d.ts +0 -9
- package/dist/extension/tools/html_script.d.ts +0 -10
- package/dist/extension/tools/index.d.ts +0 -12
- package/dist/extension/tools/open_url.d.ts +0 -19
- package/dist/extension/tools/request_login.d.ts +0 -10
- package/dist/extension/tools/screenshot.d.ts +0 -18
- package/dist/extension/tools/switch_tab.d.ts +0 -8
- package/dist/extension/tools/tab_management.d.ts +0 -13
- package/dist/extension/tools/tool_returns_screenshot.d.ts +0 -8
- package/dist/extension/tools/web_search.d.ts +0 -18
- package/dist/extension/utils.d.ts +0 -31
- package/dist/extension.cjs.js +0 -2793
- package/dist/extension.esm.js +0 -2786
- package/dist/extension_content_script.js +0 -1078
- package/dist/fellou/computer.d.ts +0 -20
- package/dist/fellou/index.d.ts +0 -6
- package/dist/fellou/tools/computer_use.d.ts +0 -18
- package/dist/fellou.cjs.js +0 -238
- package/dist/fellou.esm.js +0 -235
- package/dist/models/action.d.ts +0 -33
- package/dist/models/workflow.d.ts +0 -25
- package/dist/nodejs/core.d.ts +0 -2
- package/dist/nodejs/index.d.ts +0 -3
- package/dist/nodejs/script/build_dom_tree.d.ts +0 -1
- package/dist/nodejs/tools/browser_use.d.ts +0 -28
- package/dist/nodejs/tools/command_execute.d.ts +0 -12
- package/dist/nodejs/tools/file_read.d.ts +0 -11
- package/dist/nodejs/tools/file_write.d.ts +0 -15
- package/dist/nodejs/tools/index.d.ts +0 -5
- package/dist/nodejs.cjs.js +0 -73507
- package/dist/nodejs.esm.js +0 -73504
- package/dist/schemas/workflow.schema.d.ts +0 -44
- package/dist/services/llm/claude-provider.d.ts +0 -12
- package/dist/services/llm/openai-provider.d.ts +0 -12
- package/dist/services/llm/provider-factory.d.ts +0 -4
- package/dist/services/parser/workflow-parser.d.ts +0 -23
- package/dist/services/workflow/generator.d.ts +0 -16
- package/dist/services/workflow/templates.d.ts +0 -8
- package/dist/types/action.types.d.ts +0 -53
- package/dist/types/eko.types.d.ts +0 -37
- package/dist/types/parser.types.d.ts +0 -9
- package/dist/types/workflow.types.d.ts +0 -57
- package/dist/utils/execution-logger.d.ts +0 -69
- package/dist/utils/sleep.d.ts +0 -1
- package/dist/web/core.d.ts +0 -2
- package/dist/web/index.d.ts +0 -5
- package/dist/web/script/build_dom_tree.d.ts +0 -10
- package/dist/web/tools/browser.d.ts +0 -21
- package/dist/web/tools/browser_use.d.ts +0 -19
- package/dist/web/tools/export_file.d.ts +0 -18
- package/dist/web/tools/extract_content.d.ts +0 -17
- package/dist/web/tools/html_script.d.ts +0 -10
- package/dist/web/tools/index.d.ts +0 -6
- package/dist/web/tools/screenshot.d.ts +0 -18
- package/dist/web.cjs.js +0 -9952
- package/dist/web.esm.js +0 -9948
package/dist/extension.cjs.js
DELETED
|
@@ -1,2793 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var os = require('os');
|
|
4
|
-
var path = require('path');
|
|
5
|
-
var util = require('util');
|
|
6
|
-
|
|
7
|
-
const prettyLogStyles = {
|
|
8
|
-
reset: [0, 0],
|
|
9
|
-
bold: [1, 22],
|
|
10
|
-
dim: [2, 22],
|
|
11
|
-
italic: [3, 23],
|
|
12
|
-
underline: [4, 24],
|
|
13
|
-
overline: [53, 55],
|
|
14
|
-
inverse: [7, 27],
|
|
15
|
-
hidden: [8, 28],
|
|
16
|
-
strikethrough: [9, 29],
|
|
17
|
-
black: [30, 39],
|
|
18
|
-
red: [31, 39],
|
|
19
|
-
green: [32, 39],
|
|
20
|
-
yellow: [33, 39],
|
|
21
|
-
blue: [34, 39],
|
|
22
|
-
magenta: [35, 39],
|
|
23
|
-
cyan: [36, 39],
|
|
24
|
-
white: [37, 39],
|
|
25
|
-
blackBright: [90, 39],
|
|
26
|
-
redBright: [91, 39],
|
|
27
|
-
greenBright: [92, 39],
|
|
28
|
-
yellowBright: [93, 39],
|
|
29
|
-
blueBright: [94, 39],
|
|
30
|
-
magentaBright: [95, 39],
|
|
31
|
-
cyanBright: [96, 39],
|
|
32
|
-
whiteBright: [97, 39],
|
|
33
|
-
bgBlack: [40, 49],
|
|
34
|
-
bgRed: [41, 49],
|
|
35
|
-
bgGreen: [42, 49],
|
|
36
|
-
bgYellow: [43, 49],
|
|
37
|
-
bgBlue: [44, 49],
|
|
38
|
-
bgMagenta: [45, 49],
|
|
39
|
-
bgCyan: [46, 49],
|
|
40
|
-
bgWhite: [47, 49],
|
|
41
|
-
bgBlackBright: [100, 49],
|
|
42
|
-
bgRedBright: [101, 49],
|
|
43
|
-
bgGreenBright: [102, 49],
|
|
44
|
-
bgYellowBright: [103, 49],
|
|
45
|
-
bgBlueBright: [104, 49],
|
|
46
|
-
bgMagentaBright: [105, 49],
|
|
47
|
-
bgCyanBright: [106, 49],
|
|
48
|
-
bgWhiteBright: [107, 49],
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
function formatTemplate(settings, template, values, hideUnsetPlaceholder = false) {
|
|
52
|
-
const templateString = String(template);
|
|
53
|
-
const ansiColorWrap = (placeholderValue, code) => `\u001b[${code[0]}m${placeholderValue}\u001b[${code[1]}m`;
|
|
54
|
-
const styleWrap = (value, style) => {
|
|
55
|
-
if (style != null && typeof style === "string") {
|
|
56
|
-
return ansiColorWrap(value, prettyLogStyles[style]);
|
|
57
|
-
}
|
|
58
|
-
else if (style != null && Array.isArray(style)) {
|
|
59
|
-
return style.reduce((prevValue, thisStyle) => styleWrap(prevValue, thisStyle), value);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
if (style != null && style[value.trim()] != null) {
|
|
63
|
-
return styleWrap(value, style[value.trim()]);
|
|
64
|
-
}
|
|
65
|
-
else if (style != null && style["*"] != null) {
|
|
66
|
-
return styleWrap(value, style["*"]);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
const defaultStyle = null;
|
|
74
|
-
return templateString.replace(/{{(.+?)}}/g, (_, placeholder) => {
|
|
75
|
-
const value = values[placeholder] != null ? String(values[placeholder]) : hideUnsetPlaceholder ? "" : _;
|
|
76
|
-
return settings.stylePrettyLogs
|
|
77
|
-
? styleWrap(value, settings?.prettyLogStyles?.[placeholder] ?? defaultStyle) + ansiColorWrap("", prettyLogStyles.reset)
|
|
78
|
-
: value;
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function formatNumberAddZeros(value, digits = 2, addNumber = 0) {
|
|
83
|
-
if (value != null && isNaN(value)) {
|
|
84
|
-
return "";
|
|
85
|
-
}
|
|
86
|
-
value = value != null ? value + addNumber : value;
|
|
87
|
-
return digits === 2
|
|
88
|
-
? value == null
|
|
89
|
-
? "--"
|
|
90
|
-
: value < 10
|
|
91
|
-
? "0" + value
|
|
92
|
-
: value.toString()
|
|
93
|
-
: value == null
|
|
94
|
-
? "---"
|
|
95
|
-
: value < 10
|
|
96
|
-
? "00" + value
|
|
97
|
-
: value < 100
|
|
98
|
-
? "0" + value
|
|
99
|
-
: value.toString();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function urlToObject(url) {
|
|
103
|
-
return {
|
|
104
|
-
href: url.href,
|
|
105
|
-
protocol: url.protocol,
|
|
106
|
-
username: url.username,
|
|
107
|
-
password: url.password,
|
|
108
|
-
host: url.host,
|
|
109
|
-
hostname: url.hostname,
|
|
110
|
-
port: url.port,
|
|
111
|
-
pathname: url.pathname,
|
|
112
|
-
search: url.search,
|
|
113
|
-
searchParams: [...url.searchParams].map(([key, value]) => ({ key, value })),
|
|
114
|
-
hash: url.hash,
|
|
115
|
-
origin: url.origin,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
var Runtime = {
|
|
120
|
-
getCallerStackFrame,
|
|
121
|
-
getErrorTrace,
|
|
122
|
-
getMeta,
|
|
123
|
-
transportJSON,
|
|
124
|
-
transportFormatted: transportFormatted$1,
|
|
125
|
-
isBuffer,
|
|
126
|
-
isError,
|
|
127
|
-
prettyFormatLogObj,
|
|
128
|
-
prettyFormatErrorObj,
|
|
129
|
-
};
|
|
130
|
-
const meta = {
|
|
131
|
-
runtime: "Nodejs",
|
|
132
|
-
runtimeVersion: process?.version,
|
|
133
|
-
hostname: os.hostname ? os.hostname() : undefined,
|
|
134
|
-
};
|
|
135
|
-
function getMeta(logLevelId, logLevelName, stackDepthLevel, hideLogPositionForPerformance, name, parentNames) {
|
|
136
|
-
return Object.assign({}, meta, {
|
|
137
|
-
name,
|
|
138
|
-
parentNames,
|
|
139
|
-
date: new Date(),
|
|
140
|
-
logLevelId,
|
|
141
|
-
logLevelName,
|
|
142
|
-
path: !hideLogPositionForPerformance ? getCallerStackFrame(stackDepthLevel) : undefined,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
function getCallerStackFrame(stackDepthLevel, error = Error()) {
|
|
146
|
-
return stackLineToStackFrame(error?.stack?.split("\n")?.filter((thisLine) => thisLine.includes(" at "))?.[stackDepthLevel]);
|
|
147
|
-
}
|
|
148
|
-
function getErrorTrace(error) {
|
|
149
|
-
return error?.stack?.split("\n")?.reduce((result, line) => {
|
|
150
|
-
if (line.includes(" at ")) {
|
|
151
|
-
result.push(stackLineToStackFrame(line));
|
|
152
|
-
}
|
|
153
|
-
return result;
|
|
154
|
-
}, []);
|
|
155
|
-
}
|
|
156
|
-
function stackLineToStackFrame(line) {
|
|
157
|
-
const pathResult = {
|
|
158
|
-
fullFilePath: undefined,
|
|
159
|
-
fileName: undefined,
|
|
160
|
-
fileNameWithLine: undefined,
|
|
161
|
-
fileColumn: undefined,
|
|
162
|
-
fileLine: undefined,
|
|
163
|
-
filePath: undefined,
|
|
164
|
-
filePathWithLine: undefined,
|
|
165
|
-
method: undefined,
|
|
166
|
-
};
|
|
167
|
-
if (line != null && line.includes(" at ")) {
|
|
168
|
-
line = line.replace(/^\s+at\s+/gm, "");
|
|
169
|
-
const errorStackLine = line.split(" (");
|
|
170
|
-
const fullFilePath = line?.slice(-1) === ")" ? line?.match(/\(([^)]+)\)/)?.[1] : line;
|
|
171
|
-
const pathArray = fullFilePath?.includes(":") ? fullFilePath?.replace("file://", "")?.replace(process.cwd(), "")?.split(":") : undefined;
|
|
172
|
-
const fileColumn = pathArray?.pop();
|
|
173
|
-
const fileLine = pathArray?.pop();
|
|
174
|
-
const filePath = pathArray?.pop();
|
|
175
|
-
const filePathWithLine = path.normalize(`${filePath}:${fileLine}`);
|
|
176
|
-
const fileName = filePath?.split("/")?.pop();
|
|
177
|
-
const fileNameWithLine = `${fileName}:${fileLine}`;
|
|
178
|
-
if (filePath != null && filePath.length > 0) {
|
|
179
|
-
pathResult.fullFilePath = fullFilePath;
|
|
180
|
-
pathResult.fileName = fileName;
|
|
181
|
-
pathResult.fileNameWithLine = fileNameWithLine;
|
|
182
|
-
pathResult.fileColumn = fileColumn;
|
|
183
|
-
pathResult.fileLine = fileLine;
|
|
184
|
-
pathResult.filePath = filePath;
|
|
185
|
-
pathResult.filePathWithLine = filePathWithLine;
|
|
186
|
-
pathResult.method = errorStackLine?.[1] != null ? errorStackLine?.[0] : undefined;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return pathResult;
|
|
190
|
-
}
|
|
191
|
-
function isError(e) {
|
|
192
|
-
return util.types?.isNativeError != null ? util.types.isNativeError(e) : e instanceof Error;
|
|
193
|
-
}
|
|
194
|
-
function prettyFormatLogObj(maskedArgs, settings) {
|
|
195
|
-
return maskedArgs.reduce((result, arg) => {
|
|
196
|
-
isError(arg) ? result.errors.push(prettyFormatErrorObj(arg, settings)) : result.args.push(arg);
|
|
197
|
-
return result;
|
|
198
|
-
}, { args: [], errors: [] });
|
|
199
|
-
}
|
|
200
|
-
function prettyFormatErrorObj(error, settings) {
|
|
201
|
-
const errorStackStr = getErrorTrace(error).map((stackFrame) => {
|
|
202
|
-
return formatTemplate(settings, settings.prettyErrorStackTemplate, { ...stackFrame }, true);
|
|
203
|
-
});
|
|
204
|
-
const placeholderValuesError = {
|
|
205
|
-
errorName: ` ${error.name} `,
|
|
206
|
-
errorMessage: Object.getOwnPropertyNames(error)
|
|
207
|
-
.reduce((result, key) => {
|
|
208
|
-
if (key !== "stack") {
|
|
209
|
-
result.push(error[key]);
|
|
210
|
-
}
|
|
211
|
-
return result;
|
|
212
|
-
}, [])
|
|
213
|
-
.join(", "),
|
|
214
|
-
errorStack: errorStackStr.join("\n"),
|
|
215
|
-
};
|
|
216
|
-
return formatTemplate(settings, settings.prettyErrorTemplate, placeholderValuesError);
|
|
217
|
-
}
|
|
218
|
-
function transportFormatted$1(logMetaMarkup, logArgs, logErrors, settings) {
|
|
219
|
-
const logErrorsStr = (logErrors.length > 0 && logArgs.length > 0 ? "\n" : "") + logErrors.join("\n");
|
|
220
|
-
settings.prettyInspectOptions.colors = settings.stylePrettyLogs;
|
|
221
|
-
console.log(logMetaMarkup + util.formatWithOptions(settings.prettyInspectOptions, ...logArgs) + logErrorsStr);
|
|
222
|
-
}
|
|
223
|
-
function transportJSON(json) {
|
|
224
|
-
console.log(jsonStringifyRecursive(json));
|
|
225
|
-
function jsonStringifyRecursive(obj) {
|
|
226
|
-
const cache = new Set();
|
|
227
|
-
return JSON.stringify(obj, (key, value) => {
|
|
228
|
-
if (typeof value === "object" && value !== null) {
|
|
229
|
-
if (cache.has(value)) {
|
|
230
|
-
return "[Circular]";
|
|
231
|
-
}
|
|
232
|
-
cache.add(value);
|
|
233
|
-
}
|
|
234
|
-
if (typeof value === "bigint") {
|
|
235
|
-
return `${value}`;
|
|
236
|
-
}
|
|
237
|
-
if (typeof value === "undefined") {
|
|
238
|
-
return "[undefined]";
|
|
239
|
-
}
|
|
240
|
-
return value;
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
function isBuffer(arg) {
|
|
245
|
-
return Buffer.isBuffer(arg);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
class BaseLogger {
|
|
249
|
-
constructor(settings, logObj, stackDepthLevel = 4) {
|
|
250
|
-
this.logObj = logObj;
|
|
251
|
-
this.stackDepthLevel = stackDepthLevel;
|
|
252
|
-
this.runtime = Runtime;
|
|
253
|
-
this.settings = {
|
|
254
|
-
type: settings?.type ?? "pretty",
|
|
255
|
-
name: settings?.name,
|
|
256
|
-
parentNames: settings?.parentNames,
|
|
257
|
-
minLevel: settings?.minLevel ?? 0,
|
|
258
|
-
argumentsArrayName: settings?.argumentsArrayName,
|
|
259
|
-
hideLogPositionForProduction: settings?.hideLogPositionForProduction ?? false,
|
|
260
|
-
prettyLogTemplate: settings?.prettyLogTemplate ??
|
|
261
|
-
"{{yyyy}}.{{mm}}.{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}}\t{{logLevelName}}\t{{filePathWithLine}}{{nameWithDelimiterPrefix}}\t",
|
|
262
|
-
prettyErrorTemplate: settings?.prettyErrorTemplate ?? "\n{{errorName}} {{errorMessage}}\nerror stack:\n{{errorStack}}",
|
|
263
|
-
prettyErrorStackTemplate: settings?.prettyErrorStackTemplate ?? " • {{fileName}}\t{{method}}\n\t{{filePathWithLine}}",
|
|
264
|
-
prettyErrorParentNamesSeparator: settings?.prettyErrorParentNamesSeparator ?? ":",
|
|
265
|
-
prettyErrorLoggerNameDelimiter: settings?.prettyErrorLoggerNameDelimiter ?? "\t",
|
|
266
|
-
stylePrettyLogs: settings?.stylePrettyLogs ?? true,
|
|
267
|
-
prettyLogTimeZone: settings?.prettyLogTimeZone ?? "UTC",
|
|
268
|
-
prettyLogStyles: settings?.prettyLogStyles ?? {
|
|
269
|
-
logLevelName: {
|
|
270
|
-
"*": ["bold", "black", "bgWhiteBright", "dim"],
|
|
271
|
-
SILLY: ["bold", "white"],
|
|
272
|
-
TRACE: ["bold", "whiteBright"],
|
|
273
|
-
DEBUG: ["bold", "green"],
|
|
274
|
-
INFO: ["bold", "blue"],
|
|
275
|
-
WARN: ["bold", "yellow"],
|
|
276
|
-
ERROR: ["bold", "red"],
|
|
277
|
-
FATAL: ["bold", "redBright"],
|
|
278
|
-
},
|
|
279
|
-
dateIsoStr: "white",
|
|
280
|
-
filePathWithLine: "white",
|
|
281
|
-
name: ["white", "bold"],
|
|
282
|
-
nameWithDelimiterPrefix: ["white", "bold"],
|
|
283
|
-
nameWithDelimiterSuffix: ["white", "bold"],
|
|
284
|
-
errorName: ["bold", "bgRedBright", "whiteBright"],
|
|
285
|
-
fileName: ["yellow"],
|
|
286
|
-
fileNameWithLine: "white",
|
|
287
|
-
},
|
|
288
|
-
prettyInspectOptions: settings?.prettyInspectOptions ?? {
|
|
289
|
-
colors: true,
|
|
290
|
-
compact: false,
|
|
291
|
-
depth: Infinity,
|
|
292
|
-
},
|
|
293
|
-
metaProperty: settings?.metaProperty ?? "_meta",
|
|
294
|
-
maskPlaceholder: settings?.maskPlaceholder ?? "[***]",
|
|
295
|
-
maskValuesOfKeys: settings?.maskValuesOfKeys ?? ["password"],
|
|
296
|
-
maskValuesOfKeysCaseInsensitive: settings?.maskValuesOfKeysCaseInsensitive ?? false,
|
|
297
|
-
maskValuesRegEx: settings?.maskValuesRegEx,
|
|
298
|
-
prefix: [...(settings?.prefix ?? [])],
|
|
299
|
-
attachedTransports: [...(settings?.attachedTransports ?? [])],
|
|
300
|
-
overwrite: {
|
|
301
|
-
mask: settings?.overwrite?.mask,
|
|
302
|
-
toLogObj: settings?.overwrite?.toLogObj,
|
|
303
|
-
addMeta: settings?.overwrite?.addMeta,
|
|
304
|
-
addPlaceholders: settings?.overwrite?.addPlaceholders,
|
|
305
|
-
formatMeta: settings?.overwrite?.formatMeta,
|
|
306
|
-
formatLogObj: settings?.overwrite?.formatLogObj,
|
|
307
|
-
transportFormatted: settings?.overwrite?.transportFormatted,
|
|
308
|
-
transportJSON: settings?.overwrite?.transportJSON,
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
log(logLevelId, logLevelName, ...args) {
|
|
313
|
-
if (logLevelId < this.settings.minLevel) {
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
const logArgs = [...this.settings.prefix, ...args];
|
|
317
|
-
const maskedArgs = this.settings.overwrite?.mask != null
|
|
318
|
-
? this.settings.overwrite?.mask(logArgs)
|
|
319
|
-
: this.settings.maskValuesOfKeys != null && this.settings.maskValuesOfKeys.length > 0
|
|
320
|
-
? this._mask(logArgs)
|
|
321
|
-
: logArgs;
|
|
322
|
-
const thisLogObj = this.logObj != null ? this._recursiveCloneAndExecuteFunctions(this.logObj) : undefined;
|
|
323
|
-
const logObj = this.settings.overwrite?.toLogObj != null ? this.settings.overwrite?.toLogObj(maskedArgs, thisLogObj) : this._toLogObj(maskedArgs, thisLogObj);
|
|
324
|
-
const logObjWithMeta = this.settings.overwrite?.addMeta != null
|
|
325
|
-
? this.settings.overwrite?.addMeta(logObj, logLevelId, logLevelName)
|
|
326
|
-
: this._addMetaToLogObj(logObj, logLevelId, logLevelName);
|
|
327
|
-
let logMetaMarkup;
|
|
328
|
-
let logArgsAndErrorsMarkup = undefined;
|
|
329
|
-
if (this.settings.overwrite?.formatMeta != null) {
|
|
330
|
-
logMetaMarkup = this.settings.overwrite?.formatMeta(logObjWithMeta?.[this.settings.metaProperty]);
|
|
331
|
-
}
|
|
332
|
-
if (this.settings.overwrite?.formatLogObj != null) {
|
|
333
|
-
logArgsAndErrorsMarkup = this.settings.overwrite?.formatLogObj(maskedArgs, this.settings);
|
|
334
|
-
}
|
|
335
|
-
if (this.settings.type === "pretty") {
|
|
336
|
-
logMetaMarkup = logMetaMarkup ?? this._prettyFormatLogObjMeta(logObjWithMeta?.[this.settings.metaProperty]);
|
|
337
|
-
logArgsAndErrorsMarkup = logArgsAndErrorsMarkup ?? this.runtime.prettyFormatLogObj(maskedArgs, this.settings);
|
|
338
|
-
}
|
|
339
|
-
if (logMetaMarkup != null && logArgsAndErrorsMarkup != null) {
|
|
340
|
-
this.settings.overwrite?.transportFormatted != null
|
|
341
|
-
? this.settings.overwrite?.transportFormatted(logMetaMarkup, logArgsAndErrorsMarkup.args, logArgsAndErrorsMarkup.errors, this.settings)
|
|
342
|
-
: this.runtime.transportFormatted(logMetaMarkup, logArgsAndErrorsMarkup.args, logArgsAndErrorsMarkup.errors, this.settings);
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
this.settings.overwrite?.transportJSON != null
|
|
346
|
-
? this.settings.overwrite?.transportJSON(logObjWithMeta)
|
|
347
|
-
: this.settings.type !== "hidden"
|
|
348
|
-
? this.runtime.transportJSON(logObjWithMeta)
|
|
349
|
-
: undefined;
|
|
350
|
-
}
|
|
351
|
-
if (this.settings.attachedTransports != null && this.settings.attachedTransports.length > 0) {
|
|
352
|
-
this.settings.attachedTransports.forEach((transportLogger) => {
|
|
353
|
-
transportLogger(logObjWithMeta);
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
return logObjWithMeta;
|
|
357
|
-
}
|
|
358
|
-
attachTransport(transportLogger) {
|
|
359
|
-
this.settings.attachedTransports.push(transportLogger);
|
|
360
|
-
}
|
|
361
|
-
getSubLogger(settings, logObj) {
|
|
362
|
-
const subLoggerSettings = {
|
|
363
|
-
...this.settings,
|
|
364
|
-
...settings,
|
|
365
|
-
parentNames: this.settings?.parentNames != null && this.settings?.name != null
|
|
366
|
-
? [...this.settings.parentNames, this.settings.name]
|
|
367
|
-
: this.settings?.name != null
|
|
368
|
-
? [this.settings.name]
|
|
369
|
-
: undefined,
|
|
370
|
-
prefix: [...this.settings.prefix, ...(settings?.prefix ?? [])],
|
|
371
|
-
};
|
|
372
|
-
const subLogger = new this.constructor(subLoggerSettings, logObj ?? this.logObj, this.stackDepthLevel);
|
|
373
|
-
return subLogger;
|
|
374
|
-
}
|
|
375
|
-
_mask(args) {
|
|
376
|
-
const maskValuesOfKeys = this.settings.maskValuesOfKeysCaseInsensitive !== true ? this.settings.maskValuesOfKeys : this.settings.maskValuesOfKeys.map((key) => key.toLowerCase());
|
|
377
|
-
return args?.map((arg) => {
|
|
378
|
-
return this._recursiveCloneAndMaskValuesOfKeys(arg, maskValuesOfKeys);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
_recursiveCloneAndMaskValuesOfKeys(source, keys, seen = []) {
|
|
382
|
-
if (seen.includes(source)) {
|
|
383
|
-
return { ...source };
|
|
384
|
-
}
|
|
385
|
-
if (typeof source === "object" && source !== null) {
|
|
386
|
-
seen.push(source);
|
|
387
|
-
}
|
|
388
|
-
if (this.runtime.isError(source) || this.runtime.isBuffer(source)) {
|
|
389
|
-
return source;
|
|
390
|
-
}
|
|
391
|
-
else if (source instanceof Map) {
|
|
392
|
-
return new Map(source);
|
|
393
|
-
}
|
|
394
|
-
else if (source instanceof Set) {
|
|
395
|
-
return new Set(source);
|
|
396
|
-
}
|
|
397
|
-
else if (Array.isArray(source)) {
|
|
398
|
-
return source.map((item) => this._recursiveCloneAndMaskValuesOfKeys(item, keys, seen));
|
|
399
|
-
}
|
|
400
|
-
else if (source instanceof Date) {
|
|
401
|
-
return new Date(source.getTime());
|
|
402
|
-
}
|
|
403
|
-
else if (source instanceof URL) {
|
|
404
|
-
return urlToObject(source);
|
|
405
|
-
}
|
|
406
|
-
else if (source !== null && typeof source === "object") {
|
|
407
|
-
const baseObject = this.runtime.isError(source) ? this._cloneError(source) : Object.create(Object.getPrototypeOf(source));
|
|
408
|
-
return Object.getOwnPropertyNames(source).reduce((o, prop) => {
|
|
409
|
-
o[prop] = keys.includes(this.settings?.maskValuesOfKeysCaseInsensitive !== true ? prop : prop.toLowerCase())
|
|
410
|
-
? this.settings.maskPlaceholder
|
|
411
|
-
: (() => {
|
|
412
|
-
try {
|
|
413
|
-
return this._recursiveCloneAndMaskValuesOfKeys(source[prop], keys, seen);
|
|
414
|
-
}
|
|
415
|
-
catch (e) {
|
|
416
|
-
return null;
|
|
417
|
-
}
|
|
418
|
-
})();
|
|
419
|
-
return o;
|
|
420
|
-
}, baseObject);
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
if (typeof source === "string") {
|
|
424
|
-
let modifiedSource = source;
|
|
425
|
-
for (const regEx of this.settings?.maskValuesRegEx || []) {
|
|
426
|
-
modifiedSource = modifiedSource.replace(regEx, this.settings?.maskPlaceholder || "");
|
|
427
|
-
}
|
|
428
|
-
return modifiedSource;
|
|
429
|
-
}
|
|
430
|
-
return source;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
_recursiveCloneAndExecuteFunctions(source, seen = []) {
|
|
434
|
-
if (this.isObjectOrArray(source) && seen.includes(source)) {
|
|
435
|
-
return this.shallowCopy(source);
|
|
436
|
-
}
|
|
437
|
-
if (this.isObjectOrArray(source)) {
|
|
438
|
-
seen.push(source);
|
|
439
|
-
}
|
|
440
|
-
if (Array.isArray(source)) {
|
|
441
|
-
return source.map((item) => this._recursiveCloneAndExecuteFunctions(item, seen));
|
|
442
|
-
}
|
|
443
|
-
else if (source instanceof Date) {
|
|
444
|
-
return new Date(source.getTime());
|
|
445
|
-
}
|
|
446
|
-
else if (this.isObject(source)) {
|
|
447
|
-
return Object.getOwnPropertyNames(source).reduce((o, prop) => {
|
|
448
|
-
const descriptor = Object.getOwnPropertyDescriptor(source, prop);
|
|
449
|
-
if (descriptor) {
|
|
450
|
-
Object.defineProperty(o, prop, descriptor);
|
|
451
|
-
const value = source[prop];
|
|
452
|
-
o[prop] = typeof value === "function" ? value() : this._recursiveCloneAndExecuteFunctions(value, seen);
|
|
453
|
-
}
|
|
454
|
-
return o;
|
|
455
|
-
}, Object.create(Object.getPrototypeOf(source)));
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
return source;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
isObjectOrArray(value) {
|
|
462
|
-
return typeof value === "object" && value !== null;
|
|
463
|
-
}
|
|
464
|
-
isObject(value) {
|
|
465
|
-
return typeof value === "object" && !Array.isArray(value) && value !== null;
|
|
466
|
-
}
|
|
467
|
-
shallowCopy(source) {
|
|
468
|
-
if (Array.isArray(source)) {
|
|
469
|
-
return [...source];
|
|
470
|
-
}
|
|
471
|
-
else {
|
|
472
|
-
return { ...source };
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
_toLogObj(args, clonedLogObj = {}) {
|
|
476
|
-
args = args?.map((arg) => (this.runtime.isError(arg) ? this._toErrorObject(arg) : arg));
|
|
477
|
-
if (this.settings.argumentsArrayName == null) {
|
|
478
|
-
if (args.length === 1 && !Array.isArray(args[0]) && this.runtime.isBuffer(args[0]) !== true && !(args[0] instanceof Date)) {
|
|
479
|
-
clonedLogObj = typeof args[0] === "object" && args[0] != null ? { ...args[0], ...clonedLogObj } : { 0: args[0], ...clonedLogObj };
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
clonedLogObj = { ...clonedLogObj, ...args };
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
else {
|
|
486
|
-
clonedLogObj = {
|
|
487
|
-
...clonedLogObj,
|
|
488
|
-
[this.settings.argumentsArrayName]: args,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
return clonedLogObj;
|
|
492
|
-
}
|
|
493
|
-
_cloneError(error) {
|
|
494
|
-
const cloned = new error.constructor();
|
|
495
|
-
Object.getOwnPropertyNames(error).forEach((key) => {
|
|
496
|
-
cloned[key] = error[key];
|
|
497
|
-
});
|
|
498
|
-
return cloned;
|
|
499
|
-
}
|
|
500
|
-
_toErrorObject(error) {
|
|
501
|
-
return {
|
|
502
|
-
nativeError: error,
|
|
503
|
-
name: error.name ?? "Error",
|
|
504
|
-
message: error.message,
|
|
505
|
-
stack: this.runtime.getErrorTrace(error),
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
_addMetaToLogObj(logObj, logLevelId, logLevelName) {
|
|
509
|
-
return {
|
|
510
|
-
...logObj,
|
|
511
|
-
[this.settings.metaProperty]: this.runtime.getMeta(logLevelId, logLevelName, this.stackDepthLevel, this.settings.hideLogPositionForProduction, this.settings.name, this.settings.parentNames),
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
_prettyFormatLogObjMeta(logObjMeta) {
|
|
515
|
-
if (logObjMeta == null) {
|
|
516
|
-
return "";
|
|
517
|
-
}
|
|
518
|
-
let template = this.settings.prettyLogTemplate;
|
|
519
|
-
const placeholderValues = {};
|
|
520
|
-
if (template.includes("{{yyyy}}.{{mm}}.{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}}")) {
|
|
521
|
-
template = template.replace("{{yyyy}}.{{mm}}.{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}}", "{{dateIsoStr}}");
|
|
522
|
-
}
|
|
523
|
-
else {
|
|
524
|
-
if (this.settings.prettyLogTimeZone === "UTC") {
|
|
525
|
-
placeholderValues["yyyy"] = logObjMeta?.date?.getUTCFullYear() ?? "----";
|
|
526
|
-
placeholderValues["mm"] = formatNumberAddZeros(logObjMeta?.date?.getUTCMonth(), 2, 1);
|
|
527
|
-
placeholderValues["dd"] = formatNumberAddZeros(logObjMeta?.date?.getUTCDate(), 2);
|
|
528
|
-
placeholderValues["hh"] = formatNumberAddZeros(logObjMeta?.date?.getUTCHours(), 2);
|
|
529
|
-
placeholderValues["MM"] = formatNumberAddZeros(logObjMeta?.date?.getUTCMinutes(), 2);
|
|
530
|
-
placeholderValues["ss"] = formatNumberAddZeros(logObjMeta?.date?.getUTCSeconds(), 2);
|
|
531
|
-
placeholderValues["ms"] = formatNumberAddZeros(logObjMeta?.date?.getUTCMilliseconds(), 3);
|
|
532
|
-
}
|
|
533
|
-
else {
|
|
534
|
-
placeholderValues["yyyy"] = logObjMeta?.date?.getFullYear() ?? "----";
|
|
535
|
-
placeholderValues["mm"] = formatNumberAddZeros(logObjMeta?.date?.getMonth(), 2, 1);
|
|
536
|
-
placeholderValues["dd"] = formatNumberAddZeros(logObjMeta?.date?.getDate(), 2);
|
|
537
|
-
placeholderValues["hh"] = formatNumberAddZeros(logObjMeta?.date?.getHours(), 2);
|
|
538
|
-
placeholderValues["MM"] = formatNumberAddZeros(logObjMeta?.date?.getMinutes(), 2);
|
|
539
|
-
placeholderValues["ss"] = formatNumberAddZeros(logObjMeta?.date?.getSeconds(), 2);
|
|
540
|
-
placeholderValues["ms"] = formatNumberAddZeros(logObjMeta?.date?.getMilliseconds(), 3);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
const dateInSettingsTimeZone = this.settings.prettyLogTimeZone === "UTC" ? logObjMeta?.date : new Date(logObjMeta?.date?.getTime() - logObjMeta?.date?.getTimezoneOffset() * 60000);
|
|
544
|
-
placeholderValues["rawIsoStr"] = dateInSettingsTimeZone?.toISOString();
|
|
545
|
-
placeholderValues["dateIsoStr"] = dateInSettingsTimeZone?.toISOString().replace("T", " ").replace("Z", "");
|
|
546
|
-
placeholderValues["logLevelName"] = logObjMeta?.logLevelName;
|
|
547
|
-
placeholderValues["fileNameWithLine"] = logObjMeta?.path?.fileNameWithLine ?? "";
|
|
548
|
-
placeholderValues["filePathWithLine"] = logObjMeta?.path?.filePathWithLine ?? "";
|
|
549
|
-
placeholderValues["fullFilePath"] = logObjMeta?.path?.fullFilePath ?? "";
|
|
550
|
-
let parentNamesString = this.settings.parentNames?.join(this.settings.prettyErrorParentNamesSeparator);
|
|
551
|
-
parentNamesString = parentNamesString != null && logObjMeta?.name != null ? parentNamesString + this.settings.prettyErrorParentNamesSeparator : undefined;
|
|
552
|
-
placeholderValues["name"] = logObjMeta?.name != null || parentNamesString != null ? (parentNamesString ?? "") + logObjMeta?.name ?? "" : "";
|
|
553
|
-
placeholderValues["nameWithDelimiterPrefix"] =
|
|
554
|
-
placeholderValues["name"].length > 0 ? this.settings.prettyErrorLoggerNameDelimiter + placeholderValues["name"] : "";
|
|
555
|
-
placeholderValues["nameWithDelimiterSuffix"] =
|
|
556
|
-
placeholderValues["name"].length > 0 ? placeholderValues["name"] + this.settings.prettyErrorLoggerNameDelimiter : "";
|
|
557
|
-
if (this.settings.overwrite?.addPlaceholders != null) {
|
|
558
|
-
this.settings.overwrite?.addPlaceholders(logObjMeta, placeholderValues);
|
|
559
|
-
}
|
|
560
|
-
return formatTemplate(this.settings, template, placeholderValues);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
class Logger extends BaseLogger {
|
|
565
|
-
constructor(settings, logObj) {
|
|
566
|
-
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
567
|
-
const isBrowserBlinkEngine = isBrowser ? window.chrome !== undefined && window.CSS !== undefined && window.CSS.supports("color", "green") : false;
|
|
568
|
-
const isSafari = isBrowser ? /^((?!chrome|android).)*safari/i.test(navigator.userAgent) : false;
|
|
569
|
-
settings = settings || {};
|
|
570
|
-
settings.stylePrettyLogs = settings.stylePrettyLogs && isBrowser && !isBrowserBlinkEngine ? false : settings.stylePrettyLogs;
|
|
571
|
-
super(settings, logObj, isSafari ? 4 : 5);
|
|
572
|
-
}
|
|
573
|
-
log(logLevelId, logLevelName, ...args) {
|
|
574
|
-
return super.log(logLevelId, logLevelName, ...args);
|
|
575
|
-
}
|
|
576
|
-
silly(...args) {
|
|
577
|
-
return super.log(0, "SILLY", ...args);
|
|
578
|
-
}
|
|
579
|
-
trace(...args) {
|
|
580
|
-
return super.log(1, "TRACE", ...args);
|
|
581
|
-
}
|
|
582
|
-
debug(...args) {
|
|
583
|
-
return super.log(2, "DEBUG", ...args);
|
|
584
|
-
}
|
|
585
|
-
info(...args) {
|
|
586
|
-
return super.log(3, "INFO", ...args);
|
|
587
|
-
}
|
|
588
|
-
warn(...args) {
|
|
589
|
-
return super.log(4, "WARN", ...args);
|
|
590
|
-
}
|
|
591
|
-
error(...args) {
|
|
592
|
-
return super.log(5, "ERROR", ...args);
|
|
593
|
-
}
|
|
594
|
-
fatal(...args) {
|
|
595
|
-
return super.log(6, "FATAL", ...args);
|
|
596
|
-
}
|
|
597
|
-
getSubLogger(settings, logObj) {
|
|
598
|
-
return super.getSubLogger(settings, logObj);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
function transportFormatted(logMetaMarkup, logArgs, logErrors, settings) {
|
|
603
|
-
settings.prettyInspectOptions.colors = settings.stylePrettyLogs;
|
|
604
|
-
const logLevel = logMetaMarkup.trim().split(" ")[2];
|
|
605
|
-
let logFunc;
|
|
606
|
-
switch (logLevel) {
|
|
607
|
-
case "WARN":
|
|
608
|
-
logFunc = console.warn;
|
|
609
|
-
break;
|
|
610
|
-
case "ERROR":
|
|
611
|
-
case "FATAL":
|
|
612
|
-
logFunc = console.error;
|
|
613
|
-
break;
|
|
614
|
-
case "INFO":
|
|
615
|
-
logFunc = console.info;
|
|
616
|
-
break;
|
|
617
|
-
case "DEBUG":
|
|
618
|
-
case "TRACE":
|
|
619
|
-
case "SILLY":
|
|
620
|
-
default:
|
|
621
|
-
logFunc = console.debug;
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
logFunc(logMetaMarkup, ...logArgs);
|
|
625
|
-
logErrors.forEach(err => {
|
|
626
|
-
console.error(logMetaMarkup + err);
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
function formatMeta(logObjMeta) {
|
|
630
|
-
if (!logObjMeta) {
|
|
631
|
-
return '';
|
|
632
|
-
}
|
|
633
|
-
const { date, logLevelName } = logObjMeta;
|
|
634
|
-
const year = date.getFullYear();
|
|
635
|
-
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
636
|
-
const day = String(date.getDate()).padStart(2, '0');
|
|
637
|
-
const hours = String(date.getHours()).padStart(2, '0');
|
|
638
|
-
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
639
|
-
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
640
|
-
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
|
|
641
|
-
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
|
642
|
-
const loggerName = logObjMeta.name;
|
|
643
|
-
return `${formattedDate} ${logLevelName} ${loggerName}`;
|
|
644
|
-
}
|
|
645
|
-
const logger = new Logger({
|
|
646
|
-
name: "ekoLogger",
|
|
647
|
-
overwrite: {
|
|
648
|
-
transportFormatted,
|
|
649
|
-
formatMeta,
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
async function getWindowId(context) {
|
|
654
|
-
let windowId = context.variables.get('windowId');
|
|
655
|
-
if (windowId) {
|
|
656
|
-
try {
|
|
657
|
-
await context.ekoConfig.chromeProxy.windows.get(windowId);
|
|
658
|
-
}
|
|
659
|
-
catch (e) {
|
|
660
|
-
windowId = null;
|
|
661
|
-
context.variables.delete('windowId');
|
|
662
|
-
let tabId = context.variables.get('tabId');
|
|
663
|
-
if (tabId) {
|
|
664
|
-
try {
|
|
665
|
-
let tab = await context.ekoConfig.chromeProxy.tabs.get(tabId);
|
|
666
|
-
windowId = tab.windowId;
|
|
667
|
-
}
|
|
668
|
-
catch (e) {
|
|
669
|
-
context.variables.delete('tabId');
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
if (!windowId) {
|
|
675
|
-
const window = await context.ekoConfig.chromeProxy.windows.getCurrent();
|
|
676
|
-
windowId = window.id;
|
|
677
|
-
}
|
|
678
|
-
// `window.FELLOU_WINDOW_ID` is a feature of Downstream Caller
|
|
679
|
-
if (!windowId) {
|
|
680
|
-
windowId = window.FELLOU_WINDOW_ID;
|
|
681
|
-
}
|
|
682
|
-
if (!windowId) {
|
|
683
|
-
logger.warn("`getWindowId()` returns " + windowId);
|
|
684
|
-
}
|
|
685
|
-
return windowId;
|
|
686
|
-
}
|
|
687
|
-
async function getTabId(context) {
|
|
688
|
-
logger.debug("getTabId()...");
|
|
689
|
-
let tabs = await context.ekoConfig.chromeProxy.tabs.query({});
|
|
690
|
-
logger.debug("all tabs:", tabs);
|
|
691
|
-
const filtered = tabs.filter((tab) => tab.title && tab.url);
|
|
692
|
-
logger.debug("filtered:", filtered);
|
|
693
|
-
if (filtered.length > 0) {
|
|
694
|
-
if (typeof filtered[0].activeTime != "undefined") {
|
|
695
|
-
const sorted = filtered.sort((a, b) => parseInt(b.activeTime) - parseInt(a.activeTime));
|
|
696
|
-
logger.debug("sorted tabs:", sorted);
|
|
697
|
-
const tabId = sorted[0].id;
|
|
698
|
-
logger.debug("tabId:", tabId);
|
|
699
|
-
return tabId;
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
tabs = await context.ekoConfig.chromeProxy.tabs.query({ active: true, currentWindow: true });
|
|
703
|
-
if (tabs.length > 0) {
|
|
704
|
-
const tabId = tabs[0].id;
|
|
705
|
-
logger.debug("tabId:", tabId);
|
|
706
|
-
return tabId;
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
throw Error("no active tab found");
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
else {
|
|
714
|
-
throw Error("no tab found");
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
function getCurrentTabId(chromeProxy, windowId) {
|
|
718
|
-
return new Promise((resolve, reject) => {
|
|
719
|
-
logger.debug("debug the Promise in getCurrentTabId()...");
|
|
720
|
-
logger.debug("get the active tabId on: ", { windowId });
|
|
721
|
-
let queryInfo;
|
|
722
|
-
if (windowId !== undefined) {
|
|
723
|
-
logger.debug(`get the active tab in window (windowId=${windowId})...`);
|
|
724
|
-
queryInfo = { windowId, active: true };
|
|
725
|
-
}
|
|
726
|
-
else {
|
|
727
|
-
logger.debug(`get the active tabId on current window`);
|
|
728
|
-
queryInfo = { active: true, currentWindow: true };
|
|
729
|
-
}
|
|
730
|
-
chromeProxy.tabs.query(queryInfo, (tabs) => {
|
|
731
|
-
if (chromeProxy.runtime.lastError) {
|
|
732
|
-
logger.error(`failed to get: `, chromeProxy.runtime.lastError);
|
|
733
|
-
reject(chromeProxy.runtime.lastError);
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
if (tabs.length > 0) {
|
|
737
|
-
logger.debug(`found the tab, ID=${tabs[0].id}`);
|
|
738
|
-
resolve(tabs[0].id);
|
|
739
|
-
}
|
|
740
|
-
else {
|
|
741
|
-
logger.debug(`cannot find the tab, returns undefined`);
|
|
742
|
-
resolve(undefined);
|
|
743
|
-
}
|
|
744
|
-
});
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
async function open_new_tab(chromeProxy, url, windowId) {
|
|
748
|
-
if (!windowId) {
|
|
749
|
-
const window = await chromeProxy.windows.getCurrent();
|
|
750
|
-
windowId = window.id;
|
|
751
|
-
}
|
|
752
|
-
logger.debug("windowId: " + windowId);
|
|
753
|
-
let tab = await chromeProxy.tabs.create({
|
|
754
|
-
url: url,
|
|
755
|
-
windowId: windowId,
|
|
756
|
-
});
|
|
757
|
-
logger.debug("chromeProxy.tabs.create() done");
|
|
758
|
-
let tabId = tab.id;
|
|
759
|
-
let completedTab = await waitForTabComplete(chromeProxy, tabId);
|
|
760
|
-
logger.debug("waitForTabComplete() done");
|
|
761
|
-
await sleep(200);
|
|
762
|
-
logger.debug("sleep() done");
|
|
763
|
-
return completedTab;
|
|
764
|
-
}
|
|
765
|
-
async function executeScript(chromeProxy, tabId, func, args) {
|
|
766
|
-
let frameResults = await chromeProxy.scripting.executeScript({
|
|
767
|
-
target: { tabId: tabId },
|
|
768
|
-
func: func,
|
|
769
|
-
args: args,
|
|
770
|
-
});
|
|
771
|
-
return frameResults[0].result;
|
|
772
|
-
}
|
|
773
|
-
async function waitForTabComplete(chromeProxy, tabId, timeout = 30000) {
|
|
774
|
-
return new Promise(async (resolve, reject) => {
|
|
775
|
-
logger.debug("debug waitForTabComplete()...");
|
|
776
|
-
const time = setTimeout(async () => {
|
|
777
|
-
logger.debug("listener(#1)=", listener);
|
|
778
|
-
chromeProxy.tabs.onUpdated.removeListener(listener);
|
|
779
|
-
logger.debug("tabId(#1)=", tabId);
|
|
780
|
-
let tab = await chromeProxy.tabs.get(tabId);
|
|
781
|
-
logger.debug("tab(#1)=", tab);
|
|
782
|
-
if (tab.status === 'complete') {
|
|
783
|
-
logger.warn('Timeout: waitForTabComplete, but tab is already complete.');
|
|
784
|
-
resolve(tab);
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
logger.warn("Timeout: waitForTabComplete, and tab is not complete");
|
|
788
|
-
resolve(tab);
|
|
789
|
-
}
|
|
790
|
-
}, timeout);
|
|
791
|
-
logger.debug("setTimeout done");
|
|
792
|
-
const listener = async (updatedTabId, changeInfo, tab) => {
|
|
793
|
-
logger.debug("listener start...");
|
|
794
|
-
if (updatedTabId === tabId && changeInfo.status === 'complete') {
|
|
795
|
-
logger.debug("listener(#2)=", listener);
|
|
796
|
-
chromeProxy.tabs.onUpdated.removeListener(listener);
|
|
797
|
-
clearTimeout(time);
|
|
798
|
-
resolve(tab);
|
|
799
|
-
}
|
|
800
|
-
};
|
|
801
|
-
logger.debug("tabId(#2)=", tabId);
|
|
802
|
-
let tab = await chromeProxy.tabs.get(tabId);
|
|
803
|
-
logger.debug("tab(#2)=", tab);
|
|
804
|
-
if (tab.status === 'complete') {
|
|
805
|
-
resolve(tab);
|
|
806
|
-
clearTimeout(time);
|
|
807
|
-
return;
|
|
808
|
-
}
|
|
809
|
-
logger.debug("listener(#3)=", listener);
|
|
810
|
-
chromeProxy.tabs.onUpdated.addListener(listener);
|
|
811
|
-
logger.debug("debug waitForTabComplete()...done");
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
async function doesTabExists(chromeProxy, tabId) {
|
|
815
|
-
const tabExists = await new Promise((resolve) => {
|
|
816
|
-
chromeProxy.tabs.get(tabId, (tab) => {
|
|
817
|
-
if (chromeProxy.runtime.lastError) {
|
|
818
|
-
resolve(false);
|
|
819
|
-
}
|
|
820
|
-
else {
|
|
821
|
-
resolve(true);
|
|
822
|
-
}
|
|
823
|
-
});
|
|
824
|
-
});
|
|
825
|
-
return tabExists;
|
|
826
|
-
}
|
|
827
|
-
async function getPageSize(chromeProxy, tabId) {
|
|
828
|
-
if (!tabId) {
|
|
829
|
-
tabId = await getCurrentTabId(chromeProxy);
|
|
830
|
-
}
|
|
831
|
-
let injectionResult = await chromeProxy.scripting.executeScript({
|
|
832
|
-
target: { tabId: tabId },
|
|
833
|
-
func: () => [
|
|
834
|
-
window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
|
|
835
|
-
window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
|
|
836
|
-
],
|
|
837
|
-
});
|
|
838
|
-
return [injectionResult[0].result[0], injectionResult[0].result[1]];
|
|
839
|
-
}
|
|
840
|
-
function sleep(time) {
|
|
841
|
-
return new Promise((resolve) => setTimeout(() => resolve(), time));
|
|
842
|
-
}
|
|
843
|
-
async function injectScript(chromeProxy, tabId, filename) {
|
|
844
|
-
let files = ['eko/script/common.js'];
|
|
845
|
-
if (filename) {
|
|
846
|
-
files.push('eko/script/' + filename);
|
|
847
|
-
}
|
|
848
|
-
await chromeProxy.scripting.executeScript({
|
|
849
|
-
target: { tabId },
|
|
850
|
-
files: files,
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
class MsgEvent {
|
|
854
|
-
constructor() {
|
|
855
|
-
this.eventMap = {};
|
|
856
|
-
}
|
|
857
|
-
addListener(callback, id) {
|
|
858
|
-
if (!id) {
|
|
859
|
-
id = new Date().getTime() + '' + Math.floor(Math.random() * 10000);
|
|
860
|
-
}
|
|
861
|
-
this.eventMap[id] = callback;
|
|
862
|
-
return id;
|
|
863
|
-
}
|
|
864
|
-
removeListener(id) {
|
|
865
|
-
delete this.eventMap[id];
|
|
866
|
-
}
|
|
867
|
-
async publish(msg) {
|
|
868
|
-
let values = Object.values(this.eventMap);
|
|
869
|
-
for (let i = 0; i < values.length; i++) {
|
|
870
|
-
try {
|
|
871
|
-
let result = values[i](msg);
|
|
872
|
-
if (isPromise(result)) {
|
|
873
|
-
await result;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
catch (e) {
|
|
877
|
-
logger.error(e);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Counter (Function: Wait for all asynchronous tasks to complete)
|
|
884
|
-
*/
|
|
885
|
-
class CountDownLatch {
|
|
886
|
-
constructor(count) {
|
|
887
|
-
this.resolve = undefined;
|
|
888
|
-
this.currentCount = count;
|
|
889
|
-
}
|
|
890
|
-
countDown() {
|
|
891
|
-
this.currentCount = this.currentCount - 1;
|
|
892
|
-
if (this.currentCount <= 0) {
|
|
893
|
-
this.resolve && this.resolve();
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
await(timeout) {
|
|
897
|
-
const $this = this;
|
|
898
|
-
return new Promise((_resolve, reject) => {
|
|
899
|
-
let resolve = _resolve;
|
|
900
|
-
if (timeout > 0) {
|
|
901
|
-
let timeId = setTimeout(reject, timeout);
|
|
902
|
-
resolve = () => {
|
|
903
|
-
clearTimeout(timeId);
|
|
904
|
-
_resolve();
|
|
905
|
-
};
|
|
906
|
-
}
|
|
907
|
-
$this.resolve = resolve;
|
|
908
|
-
if ($this.currentCount <= 0) {
|
|
909
|
-
resolve();
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
function isPromise(obj) {
|
|
915
|
-
return (!!obj &&
|
|
916
|
-
(typeof obj === 'object' || typeof obj === 'function') &&
|
|
917
|
-
typeof obj.then === 'function');
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
var utils = /*#__PURE__*/Object.freeze({
|
|
921
|
-
__proto__: null,
|
|
922
|
-
CountDownLatch: CountDownLatch,
|
|
923
|
-
MsgEvent: MsgEvent,
|
|
924
|
-
doesTabExists: doesTabExists,
|
|
925
|
-
executeScript: executeScript,
|
|
926
|
-
getCurrentTabId: getCurrentTabId,
|
|
927
|
-
getPageSize: getPageSize,
|
|
928
|
-
getTabId: getTabId,
|
|
929
|
-
getWindowId: getWindowId,
|
|
930
|
-
injectScript: injectScript,
|
|
931
|
-
isPromise: isPromise,
|
|
932
|
-
open_new_tab: open_new_tab,
|
|
933
|
-
sleep: sleep,
|
|
934
|
-
waitForTabComplete: waitForTabComplete
|
|
935
|
-
});
|
|
936
|
-
|
|
937
|
-
function isFellouBrowser(chromeProxy) {
|
|
938
|
-
const result = typeof chromeProxy.browseruse == 'object';
|
|
939
|
-
logger.debug("isFellouBrowser", result);
|
|
940
|
-
return result;
|
|
941
|
-
}
|
|
942
|
-
async function type(chromeProxy, tabId, text, coordinate) {
|
|
943
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
944
|
-
logger.debug('Sending type message to tab:', tabId, { text, coordinate }, isFellou ? ' > fellou' : '');
|
|
945
|
-
try {
|
|
946
|
-
if (!coordinate) {
|
|
947
|
-
coordinate = (await cursor_position(chromeProxy, tabId)).coordinate;
|
|
948
|
-
}
|
|
949
|
-
await mouse_move(chromeProxy, tabId, coordinate);
|
|
950
|
-
let response;
|
|
951
|
-
if (isFellou) {
|
|
952
|
-
let enter = false;
|
|
953
|
-
if (text.endsWith('\n')) {
|
|
954
|
-
enter = true;
|
|
955
|
-
text = text.substring(0, text.length - 1);
|
|
956
|
-
}
|
|
957
|
-
response = await chromeProxy.browseruse.type(tabId, text);
|
|
958
|
-
if (enter) {
|
|
959
|
-
await chromeProxy.browseruse.keyboard.press(tabId, 'Enter');
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
else {
|
|
963
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
964
|
-
type: 'computer:type',
|
|
965
|
-
text,
|
|
966
|
-
coordinate,
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
logger.debug('type Got response:', response);
|
|
970
|
-
return response;
|
|
971
|
-
}
|
|
972
|
-
catch (e) {
|
|
973
|
-
logger.error('Failed to send type message:', e);
|
|
974
|
-
throw e;
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
async function type_by(chromeProxy, tabId, text, xpath, highlightIndex) {
|
|
978
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
979
|
-
logger.debug('Sending type_by message to tab:', tabId, { text, xpath, highlightIndex }, isFellou ? ' > fellou' : '');
|
|
980
|
-
try {
|
|
981
|
-
let response;
|
|
982
|
-
if (isFellou) {
|
|
983
|
-
let enter = false;
|
|
984
|
-
if (text.endsWith('\n')) {
|
|
985
|
-
enter = true;
|
|
986
|
-
text = text.substring(0, text.length - 1);
|
|
987
|
-
}
|
|
988
|
-
response = await chromeProxy.browseruse.handle.type(tabId, build_fellou_handle_js(xpath, highlightIndex), text);
|
|
989
|
-
if (enter) {
|
|
990
|
-
await chromeProxy.browseruse.keyboard.press(tabId, 'Enter');
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
else {
|
|
994
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
995
|
-
type: 'computer:type',
|
|
996
|
-
text,
|
|
997
|
-
xpath,
|
|
998
|
-
highlightIndex,
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
logger.debug('type_by Got response:', response);
|
|
1002
|
-
return response;
|
|
1003
|
-
}
|
|
1004
|
-
catch (e) {
|
|
1005
|
-
logger.error('Failed to send type message:', e);
|
|
1006
|
-
throw e;
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
async function enter_by(chromeProxy, tabId, xpath, highlightIndex) {
|
|
1010
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1011
|
-
logger.debug('Sending enter_by message to tab:', tabId, { xpath, highlightIndex }, isFellou ? ' > fellou' : '');
|
|
1012
|
-
try {
|
|
1013
|
-
let response;
|
|
1014
|
-
if (isFellou) {
|
|
1015
|
-
response = await chromeProxy.browseruse.keyboard.press(tabId, 'Enter');
|
|
1016
|
-
}
|
|
1017
|
-
else {
|
|
1018
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1019
|
-
type: 'computer:type',
|
|
1020
|
-
text: '\n',
|
|
1021
|
-
xpath,
|
|
1022
|
-
highlightIndex,
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
logger.debug('enter_by Got response:', response);
|
|
1026
|
-
return response;
|
|
1027
|
-
}
|
|
1028
|
-
catch (e) {
|
|
1029
|
-
logger.error('Failed to send enter_by message:', e);
|
|
1030
|
-
throw e;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
async function clear_input(chromeProxy, tabId, coordinate) {
|
|
1034
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1035
|
-
logger.debug('Sending clear_input message to tab:', tabId, { coordinate }, isFellou ? ' > fellou' : '');
|
|
1036
|
-
try {
|
|
1037
|
-
if (!coordinate) {
|
|
1038
|
-
coordinate = (await cursor_position(chromeProxy, tabId)).coordinate;
|
|
1039
|
-
}
|
|
1040
|
-
await mouse_move(chromeProxy, tabId, coordinate);
|
|
1041
|
-
let response;
|
|
1042
|
-
if (isFellou) {
|
|
1043
|
-
await chromeProxy.browseruse.mouse.click(tabId, coordinate[0], coordinate[1], { count: 3 });
|
|
1044
|
-
response = await chromeProxy.browseruse.keyboard.press(tabId, 'Backspace');
|
|
1045
|
-
}
|
|
1046
|
-
else {
|
|
1047
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1048
|
-
type: 'computer:type',
|
|
1049
|
-
text: '',
|
|
1050
|
-
coordinate,
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
logger.debug('clear_input Got response:', response);
|
|
1054
|
-
return response;
|
|
1055
|
-
}
|
|
1056
|
-
catch (e) {
|
|
1057
|
-
logger.error('Failed to send clear_input message:', e);
|
|
1058
|
-
throw e;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
async function clear_input_by(chromeProxy, tabId, xpath, highlightIndex) {
|
|
1062
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1063
|
-
logger.debug('Sending clear_input_by message to tab:', tabId, { xpath, highlightIndex }, isFellou ? ' > fellou' : '');
|
|
1064
|
-
try {
|
|
1065
|
-
let response;
|
|
1066
|
-
if (isFellou) {
|
|
1067
|
-
await chromeProxy.browseruse.handle.click(tabId, build_fellou_handle_js(xpath, highlightIndex), { count: 3 });
|
|
1068
|
-
response = await chromeProxy.browseruse.keyboard.press(tabId, 'Backspace');
|
|
1069
|
-
}
|
|
1070
|
-
else {
|
|
1071
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1072
|
-
type: 'computer:type',
|
|
1073
|
-
text: '',
|
|
1074
|
-
xpath,
|
|
1075
|
-
highlightIndex,
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
logger.debug('clear_input_by Got response:', response);
|
|
1079
|
-
return response;
|
|
1080
|
-
}
|
|
1081
|
-
catch (e) {
|
|
1082
|
-
logger.error('Failed to send clear_input_by message:', e);
|
|
1083
|
-
throw e;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
async function mouse_move(chromeProxy, tabId, coordinate) {
|
|
1087
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1088
|
-
logger.debug('Sending mouse_move message to tab:', tabId, { coordinate }, isFellou ? ' > fellou' : '');
|
|
1089
|
-
let response;
|
|
1090
|
-
if (isFellou) {
|
|
1091
|
-
response = await chromeProxy.browseruse.mouse.move(tabId, coordinate[0], coordinate[1]);
|
|
1092
|
-
}
|
|
1093
|
-
else {
|
|
1094
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1095
|
-
type: 'computer:mouse_move',
|
|
1096
|
-
coordinate,
|
|
1097
|
-
});
|
|
1098
|
-
}
|
|
1099
|
-
logger.debug('mouse_move Got response:', response);
|
|
1100
|
-
return response;
|
|
1101
|
-
}
|
|
1102
|
-
async function left_click(chromeProxy, tabId, coordinate) {
|
|
1103
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1104
|
-
logger.debug('Sending left_click message to tab:', tabId, { coordinate }, isFellou ? ' > fellou' : '');
|
|
1105
|
-
if (!coordinate) {
|
|
1106
|
-
coordinate = (await cursor_position(chromeProxy, tabId)).coordinate;
|
|
1107
|
-
}
|
|
1108
|
-
let response;
|
|
1109
|
-
if (isFellou) {
|
|
1110
|
-
response = await chromeProxy.browseruse.mouse.click(tabId, coordinate[0], coordinate[1]);
|
|
1111
|
-
}
|
|
1112
|
-
else {
|
|
1113
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1114
|
-
type: 'computer:left_click',
|
|
1115
|
-
coordinate,
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
logger.debug('left_click Got response:', response);
|
|
1119
|
-
return response;
|
|
1120
|
-
}
|
|
1121
|
-
async function left_click_by(chromeProxy, tabId, xpath, highlightIndex) {
|
|
1122
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1123
|
-
logger.debug('Sending left_click_by message to tab:', tabId, { xpath, highlightIndex }, isFellou ? ' > fellou' : '');
|
|
1124
|
-
let response;
|
|
1125
|
-
if (isFellou) {
|
|
1126
|
-
response = await chromeProxy.browseruse.handle.click(tabId, build_fellou_handle_js(xpath, highlightIndex));
|
|
1127
|
-
}
|
|
1128
|
-
else {
|
|
1129
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1130
|
-
type: 'computer:left_click',
|
|
1131
|
-
xpath,
|
|
1132
|
-
highlightIndex,
|
|
1133
|
-
});
|
|
1134
|
-
}
|
|
1135
|
-
logger.debug('left_click_by Got response:', response);
|
|
1136
|
-
return response;
|
|
1137
|
-
}
|
|
1138
|
-
async function right_click(chromeProxy, tabId, coordinate) {
|
|
1139
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1140
|
-
logger.debug('Sending right_click message to tab:', tabId, { coordinate }, isFellou ? ' > fellou' : '');
|
|
1141
|
-
if (!coordinate) {
|
|
1142
|
-
coordinate = (await cursor_position(chromeProxy, tabId)).coordinate;
|
|
1143
|
-
}
|
|
1144
|
-
let response;
|
|
1145
|
-
if (isFellou) {
|
|
1146
|
-
response = await chromeProxy.browseruse.mouse.click(tabId, coordinate[0], coordinate[1], { button: 'right' });
|
|
1147
|
-
}
|
|
1148
|
-
else {
|
|
1149
|
-
await chromeProxy.tabs.sendMessage(tabId, {
|
|
1150
|
-
type: 'computer:right_click',
|
|
1151
|
-
coordinate,
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
logger.debug('right_click Got response:', response);
|
|
1155
|
-
return response;
|
|
1156
|
-
}
|
|
1157
|
-
async function right_click_by(chromeProxy, tabId, xpath, highlightIndex) {
|
|
1158
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1159
|
-
logger.debug('Sending right_click_by message to tab:', tabId, { xpath, highlightIndex }, isFellou ? ' > fellou' : '');
|
|
1160
|
-
let response;
|
|
1161
|
-
if (isFellou) {
|
|
1162
|
-
response = await chromeProxy.browseruse.handle.click(tabId, build_fellou_handle_js(xpath, highlightIndex), { button: 'right' });
|
|
1163
|
-
}
|
|
1164
|
-
else {
|
|
1165
|
-
await chromeProxy.tabs.sendMessage(tabId, {
|
|
1166
|
-
type: 'computer:right_click',
|
|
1167
|
-
xpath,
|
|
1168
|
-
highlightIndex,
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
logger.debug('right_click_by Got response:', response);
|
|
1172
|
-
return response;
|
|
1173
|
-
}
|
|
1174
|
-
async function double_click(chromeProxy, tabId, coordinate) {
|
|
1175
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1176
|
-
logger.debug('Sending double_click message to tab:', tabId, { coordinate }, isFellou ? ' > fellou' : '');
|
|
1177
|
-
if (!coordinate) {
|
|
1178
|
-
coordinate = (await cursor_position(chromeProxy, tabId)).coordinate;
|
|
1179
|
-
}
|
|
1180
|
-
let response;
|
|
1181
|
-
if (isFellou) {
|
|
1182
|
-
response = await chromeProxy.browseruse.mouse.click(tabId, coordinate[0], coordinate[1], { count: 2 });
|
|
1183
|
-
}
|
|
1184
|
-
else {
|
|
1185
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1186
|
-
type: 'computer:double_click',
|
|
1187
|
-
coordinate,
|
|
1188
|
-
});
|
|
1189
|
-
}
|
|
1190
|
-
logger.debug('double_click Got response:', response);
|
|
1191
|
-
return response;
|
|
1192
|
-
}
|
|
1193
|
-
async function double_click_by(chromeProxy, tabId, xpath, highlightIndex) {
|
|
1194
|
-
const isFellou = isFellouBrowser(chromeProxy);
|
|
1195
|
-
logger.debug('Sending double_click_by message to tab:', tabId, { xpath, highlightIndex }, isFellou ? ' > fellou' : '');
|
|
1196
|
-
let response;
|
|
1197
|
-
if (isFellou) {
|
|
1198
|
-
response = await chromeProxy.browseruse.mouse.click(tabId, build_fellou_handle_js(xpath, highlightIndex), { count: 2 });
|
|
1199
|
-
}
|
|
1200
|
-
else {
|
|
1201
|
-
response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1202
|
-
type: 'computer:double_click',
|
|
1203
|
-
xpath,
|
|
1204
|
-
highlightIndex,
|
|
1205
|
-
});
|
|
1206
|
-
}
|
|
1207
|
-
logger.debug('double_click_by Got response:', response);
|
|
1208
|
-
return response;
|
|
1209
|
-
}
|
|
1210
|
-
async function screenshot(chromeProxy, windowId, compress) {
|
|
1211
|
-
logger.debug('Taking screenshot of window:', windowId, { compress });
|
|
1212
|
-
try {
|
|
1213
|
-
let dataUrl;
|
|
1214
|
-
if (compress) {
|
|
1215
|
-
dataUrl = await chromeProxy.tabs.captureVisibleTab(windowId, {
|
|
1216
|
-
format: 'jpeg',
|
|
1217
|
-
quality: 60, // 0-100
|
|
1218
|
-
});
|
|
1219
|
-
dataUrl = await compress_image(dataUrl, 0.7, 1);
|
|
1220
|
-
}
|
|
1221
|
-
else {
|
|
1222
|
-
dataUrl = await chromeProxy.tabs.captureVisibleTab(windowId, {
|
|
1223
|
-
format: 'jpeg',
|
|
1224
|
-
quality: 50,
|
|
1225
|
-
});
|
|
1226
|
-
}
|
|
1227
|
-
let data = dataUrl.substring(dataUrl.indexOf('base64,') + 7);
|
|
1228
|
-
const result = {
|
|
1229
|
-
image: {
|
|
1230
|
-
type: 'base64',
|
|
1231
|
-
media_type: dataUrl.indexOf('image/png') > -1 ? 'image/png' : 'image/jpeg',
|
|
1232
|
-
data: data,
|
|
1233
|
-
},
|
|
1234
|
-
};
|
|
1235
|
-
logger.debug('screenshot Got screenshot result:', result);
|
|
1236
|
-
if (!data || data.length < 30) {
|
|
1237
|
-
throw new Error('image error');
|
|
1238
|
-
}
|
|
1239
|
-
return result;
|
|
1240
|
-
}
|
|
1241
|
-
catch (e) {
|
|
1242
|
-
if (isFellouBrowser(chromeProxy)) {
|
|
1243
|
-
logger.debug('Failed to take screenshot, try fellou...');
|
|
1244
|
-
const tabId = await getCurrentTabId(chromeProxy, windowId);
|
|
1245
|
-
const base64 = await chromeProxy.browseruse.screenshot(tabId, {
|
|
1246
|
-
type: 'jpeg',
|
|
1247
|
-
quality: 60,
|
|
1248
|
-
encoding: 'base64',
|
|
1249
|
-
});
|
|
1250
|
-
const result = {
|
|
1251
|
-
image: {
|
|
1252
|
-
type: 'base64',
|
|
1253
|
-
media_type: 'image/jpeg',
|
|
1254
|
-
data: base64,
|
|
1255
|
-
},
|
|
1256
|
-
};
|
|
1257
|
-
logger.debug('screenshot Got screenshot result, try fellou:', result);
|
|
1258
|
-
return result;
|
|
1259
|
-
}
|
|
1260
|
-
logger.error('Failed to take screenshot:', e);
|
|
1261
|
-
throw e;
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
async function compress_image(dataUrl, scale = 0.8, quality = 0.8) {
|
|
1265
|
-
logger.debug('Compressing image', { scale, quality });
|
|
1266
|
-
try {
|
|
1267
|
-
const bitmap = await createImageBitmap(await (await fetch(dataUrl)).blob());
|
|
1268
|
-
let width = bitmap.width * scale;
|
|
1269
|
-
let height = bitmap.height * scale;
|
|
1270
|
-
const canvas = new OffscreenCanvas(width, height);
|
|
1271
|
-
const ctx = canvas.getContext('2d');
|
|
1272
|
-
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
1273
|
-
const blob = await canvas.convertToBlob({
|
|
1274
|
-
type: 'image/jpeg',
|
|
1275
|
-
quality: quality,
|
|
1276
|
-
});
|
|
1277
|
-
return new Promise((resolve) => {
|
|
1278
|
-
const reader = new FileReader();
|
|
1279
|
-
reader.onloadend = () => {
|
|
1280
|
-
const result = reader.result;
|
|
1281
|
-
logger.debug('Got compressed image result (sliced):', result.slice(0, 200));
|
|
1282
|
-
resolve(result);
|
|
1283
|
-
};
|
|
1284
|
-
reader.onerror = () => {
|
|
1285
|
-
resolve(dataUrl);
|
|
1286
|
-
};
|
|
1287
|
-
reader.readAsDataURL(blob);
|
|
1288
|
-
});
|
|
1289
|
-
}
|
|
1290
|
-
catch (e) {
|
|
1291
|
-
logger.error('Failed to compress image:', e);
|
|
1292
|
-
return dataUrl;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
async function scroll_to(chromeProxy, tabId, coordinate) {
|
|
1296
|
-
logger.debug('Sending scroll_to message to tab:', tabId, { coordinate });
|
|
1297
|
-
let from_coordinate = (await cursor_position(chromeProxy, tabId)).coordinate;
|
|
1298
|
-
const response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1299
|
-
type: 'computer:scroll_to',
|
|
1300
|
-
from_coordinate,
|
|
1301
|
-
to_coordinate: coordinate,
|
|
1302
|
-
});
|
|
1303
|
-
logger.debug('scroll_to Got response:', response);
|
|
1304
|
-
return response;
|
|
1305
|
-
}
|
|
1306
|
-
async function scroll_to_by(chromeProxy, tabId, xpath, highlightIndex) {
|
|
1307
|
-
logger.debug('Sending scroll_to_by message to tab:', tabId, { xpath, highlightIndex });
|
|
1308
|
-
const response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1309
|
-
type: 'computer:scroll_to',
|
|
1310
|
-
xpath,
|
|
1311
|
-
highlightIndex,
|
|
1312
|
-
});
|
|
1313
|
-
logger.debug('scroll_to_by Got response:', response);
|
|
1314
|
-
return response;
|
|
1315
|
-
}
|
|
1316
|
-
async function get_dropdown_options(chromeProxy, tabId, xpath, highlightIndex) {
|
|
1317
|
-
logger.debug('Sending get_dropdown_options message to tab:', tabId, { xpath, highlightIndex });
|
|
1318
|
-
try {
|
|
1319
|
-
const response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1320
|
-
type: 'computer:get_dropdown_options',
|
|
1321
|
-
xpath,
|
|
1322
|
-
highlightIndex,
|
|
1323
|
-
});
|
|
1324
|
-
logger.debug('get_dropdown_options Got response:', response);
|
|
1325
|
-
return response;
|
|
1326
|
-
}
|
|
1327
|
-
catch (e) {
|
|
1328
|
-
logger.error('Failed to send get_dropdown_options message:', e);
|
|
1329
|
-
throw e;
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
async function select_dropdown_option(chromeProxy, tabId, text, xpath, highlightIndex) {
|
|
1333
|
-
logger.debug('Sending select_dropdown_option message to tab:', tabId, { text, xpath, highlightIndex });
|
|
1334
|
-
try {
|
|
1335
|
-
const response = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1336
|
-
type: 'computer:select_dropdown_option',
|
|
1337
|
-
text,
|
|
1338
|
-
xpath,
|
|
1339
|
-
highlightIndex,
|
|
1340
|
-
});
|
|
1341
|
-
logger.debug('select_dropdown_option Got response:', response);
|
|
1342
|
-
return response;
|
|
1343
|
-
}
|
|
1344
|
-
catch (e) {
|
|
1345
|
-
logger.error('Failed to send select_dropdown_option message:', e);
|
|
1346
|
-
throw e;
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
async function cursor_position(chromeProxy, tabId) {
|
|
1350
|
-
logger.debug('Sending cursor_position message to tab:', tabId);
|
|
1351
|
-
try {
|
|
1352
|
-
let result = await chromeProxy.tabs.sendMessage(tabId, {
|
|
1353
|
-
type: 'computer:cursor_position',
|
|
1354
|
-
});
|
|
1355
|
-
logger.debug('Got cursor position:', result.coordinate);
|
|
1356
|
-
return { coordinate: result.coordinate };
|
|
1357
|
-
}
|
|
1358
|
-
catch (e) {
|
|
1359
|
-
logger.error('Failed to send cursor_position message:', e);
|
|
1360
|
-
throw e;
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
async function size(chromeProxy, tabId) {
|
|
1364
|
-
logger.debug('Getting page size for tab:', tabId);
|
|
1365
|
-
try {
|
|
1366
|
-
const pageSize = await getPageSize(chromeProxy, tabId);
|
|
1367
|
-
logger.debug('Got page size:', pageSize);
|
|
1368
|
-
return pageSize;
|
|
1369
|
-
}
|
|
1370
|
-
catch (e) {
|
|
1371
|
-
logger.error('Failed to get page size:', e);
|
|
1372
|
-
throw e;
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
function build_fellou_handle_js(xpath, highlightIndex) {
|
|
1376
|
-
if (highlightIndex != undefined) {
|
|
1377
|
-
return `get_highlight_element(${highlightIndex})`;
|
|
1378
|
-
}
|
|
1379
|
-
else {
|
|
1380
|
-
return `document.evaluate('${xpath}', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue`;
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
var browser = /*#__PURE__*/Object.freeze({
|
|
1385
|
-
__proto__: null,
|
|
1386
|
-
clear_input: clear_input,
|
|
1387
|
-
clear_input_by: clear_input_by,
|
|
1388
|
-
compress_image: compress_image,
|
|
1389
|
-
cursor_position: cursor_position,
|
|
1390
|
-
double_click: double_click,
|
|
1391
|
-
double_click_by: double_click_by,
|
|
1392
|
-
enter_by: enter_by,
|
|
1393
|
-
get_dropdown_options: get_dropdown_options,
|
|
1394
|
-
left_click: left_click,
|
|
1395
|
-
left_click_by: left_click_by,
|
|
1396
|
-
mouse_move: mouse_move,
|
|
1397
|
-
right_click: right_click,
|
|
1398
|
-
right_click_by: right_click_by,
|
|
1399
|
-
screenshot: screenshot,
|
|
1400
|
-
scroll_to: scroll_to,
|
|
1401
|
-
scroll_to_by: scroll_to_by,
|
|
1402
|
-
select_dropdown_option: select_dropdown_option,
|
|
1403
|
-
size: size,
|
|
1404
|
-
type: type,
|
|
1405
|
-
type_by: type_by
|
|
1406
|
-
});
|
|
1407
|
-
|
|
1408
|
-
class ToolReturnsScreenshot {
|
|
1409
|
-
async execute(context, params) {
|
|
1410
|
-
const realResult = await this.realExecute(context, params);
|
|
1411
|
-
logger.debug("debug realResult...");
|
|
1412
|
-
logger.debug(realResult);
|
|
1413
|
-
await sleep(3000); // wait for page loding
|
|
1414
|
-
let instance = new BrowserAction();
|
|
1415
|
-
const image = await instance.realExecute(context, { action: "screenshot_extract_element" });
|
|
1416
|
-
return image;
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
/**
|
|
1421
|
-
* Browser Use for general
|
|
1422
|
-
*/
|
|
1423
|
-
class BrowserAction extends ToolReturnsScreenshot {
|
|
1424
|
-
constructor() {
|
|
1425
|
-
super();
|
|
1426
|
-
this.name = 'browser_action';
|
|
1427
|
-
this.description = `Use structured commands to interact with the browser, manipulating page elements through screenshots and webpage element extraction.
|
|
1428
|
-
* 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.
|
|
1429
|
-
* 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.
|
|
1430
|
-
* ELEMENT INTERACTION:
|
|
1431
|
-
- Only use indexes that exist in the provided element list
|
|
1432
|
-
- Each element has a unique index number (e.g., "[33]:<button>")
|
|
1433
|
-
- Elements marked with "[]:" are non-interactive (for context only)
|
|
1434
|
-
* NAVIGATION & ERROR HANDLING:
|
|
1435
|
-
- If no suitable elements exist, use other functions to complete the task
|
|
1436
|
-
- If stuck, try alternative approaches
|
|
1437
|
-
- Handle popups/cookies by accepting or closing them
|
|
1438
|
-
- Use scroll to find elements you are looking for`;
|
|
1439
|
-
this.input_schema = {
|
|
1440
|
-
type: 'object',
|
|
1441
|
-
properties: {
|
|
1442
|
-
action: {
|
|
1443
|
-
type: 'string',
|
|
1444
|
-
description: `The action to perform. The available actions are:
|
|
1445
|
-
* \`screenshot_extract_element\`: Take a screenshot of the web page and extract operable elements.
|
|
1446
|
-
- 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.
|
|
1447
|
-
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
1448
|
-
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
1449
|
-
- This tool can ONLY screenshot the VISIBLE content. If a complete content is required, use 'extract_content' instead.
|
|
1450
|
-
* \`input_text\`: Enter a string in the interactive element, If you need to press the Enter key, please end with '\\n'. For search tasks, you MUST end with '\\n' to simulate pressing the Enter key.
|
|
1451
|
-
* \`click\`: Click to element.
|
|
1452
|
-
* \`right_click\`: Right-click on the element.
|
|
1453
|
-
* \`double_click\`: Double-click on the element.
|
|
1454
|
-
* \`scroll_to\`: Scroll to the specified element.
|
|
1455
|
-
* \`extract_content\`: Extract the text content of the current webpage.
|
|
1456
|
-
* \`get_dropdown_options\`: Get all options from a native dropdown element.
|
|
1457
|
-
* \`select_dropdown_option\`: Select dropdown option for interactive element index by the text of the option you want to select.`,
|
|
1458
|
-
enum: [
|
|
1459
|
-
'screenshot_extract_element',
|
|
1460
|
-
'input_text',
|
|
1461
|
-
'click',
|
|
1462
|
-
'right_click',
|
|
1463
|
-
'double_click',
|
|
1464
|
-
'scroll_to',
|
|
1465
|
-
'extract_content',
|
|
1466
|
-
'get_dropdown_options',
|
|
1467
|
-
'select_dropdown_option',
|
|
1468
|
-
],
|
|
1469
|
-
},
|
|
1470
|
-
index: {
|
|
1471
|
-
type: 'integer',
|
|
1472
|
-
description: 'index of element, Operation elements must pass the corresponding index of the element',
|
|
1473
|
-
},
|
|
1474
|
-
text: {
|
|
1475
|
-
type: 'string',
|
|
1476
|
-
description: 'Required by `action=input_text` and `action=select_dropdown_option`',
|
|
1477
|
-
},
|
|
1478
|
-
},
|
|
1479
|
-
required: ['action'],
|
|
1480
|
-
};
|
|
1481
|
-
}
|
|
1482
|
-
/**
|
|
1483
|
-
* browser
|
|
1484
|
-
*
|
|
1485
|
-
* @param {*} params { action: 'input_text', index: 1, text: 'string' }
|
|
1486
|
-
* @returns > { success: true, image?: { type: 'base64', media_type: 'image/jpeg', data: '/9j...' }, text?: string }
|
|
1487
|
-
*/
|
|
1488
|
-
async realExecute(context, params) {
|
|
1489
|
-
var _a;
|
|
1490
|
-
logger.debug("debug 'browser_action'...");
|
|
1491
|
-
logger.debug(params);
|
|
1492
|
-
try {
|
|
1493
|
-
if (params === null || !params.action) {
|
|
1494
|
-
throw new Error('Invalid parameters. Expected an object with a "action" property.');
|
|
1495
|
-
}
|
|
1496
|
-
let tabId;
|
|
1497
|
-
try {
|
|
1498
|
-
tabId = await getTabId(context);
|
|
1499
|
-
logger.debug(tabId);
|
|
1500
|
-
if (!tabId || !Number.isInteger(tabId)) {
|
|
1501
|
-
throw new Error('Could not get valid tab ID');
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
catch (e) {
|
|
1505
|
-
logger.error('Tab ID error:', e);
|
|
1506
|
-
return { success: false, error: 'Could not access browser tab' };
|
|
1507
|
-
}
|
|
1508
|
-
let windowId = await getWindowId(context);
|
|
1509
|
-
let selector_map = context.selector_map;
|
|
1510
|
-
let selector_xpath;
|
|
1511
|
-
if (params.index != null && selector_map) {
|
|
1512
|
-
selector_xpath = (_a = selector_map[params.index]) === null || _a === void 0 ? void 0 : _a.xpath;
|
|
1513
|
-
if (!selector_xpath) {
|
|
1514
|
-
throw new Error('Element does not exist');
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
let result;
|
|
1518
|
-
logger.debug("switch cases...");
|
|
1519
|
-
switch (params.action) {
|
|
1520
|
-
case 'input_text':
|
|
1521
|
-
if (params.index == null) {
|
|
1522
|
-
throw new Error('index parameter is required');
|
|
1523
|
-
}
|
|
1524
|
-
if (params.text == null) {
|
|
1525
|
-
throw new Error('text parameter is required');
|
|
1526
|
-
}
|
|
1527
|
-
await clear_input_by(context.ekoConfig.chromeProxy, tabId, selector_xpath, params.index);
|
|
1528
|
-
result = await type_by(context.ekoConfig.chromeProxy, tabId, params.text, selector_xpath, params.index);
|
|
1529
|
-
await sleep(200);
|
|
1530
|
-
break;
|
|
1531
|
-
case 'click':
|
|
1532
|
-
if (params.index == null) {
|
|
1533
|
-
throw new Error('index parameter is required');
|
|
1534
|
-
}
|
|
1535
|
-
result = await left_click_by(context.ekoConfig.chromeProxy, tabId, selector_xpath, params.index);
|
|
1536
|
-
await sleep(100);
|
|
1537
|
-
break;
|
|
1538
|
-
case 'right_click':
|
|
1539
|
-
if (params.index == null) {
|
|
1540
|
-
throw new Error('index parameter is required');
|
|
1541
|
-
}
|
|
1542
|
-
result = await right_click_by(context.ekoConfig.chromeProxy, tabId, selector_xpath, params.index);
|
|
1543
|
-
await sleep(100);
|
|
1544
|
-
break;
|
|
1545
|
-
case 'double_click':
|
|
1546
|
-
if (params.index == null) {
|
|
1547
|
-
throw new Error('index parameter is required');
|
|
1548
|
-
}
|
|
1549
|
-
result = await double_click_by(context.ekoConfig.chromeProxy, tabId, selector_xpath, params.index);
|
|
1550
|
-
await sleep(100);
|
|
1551
|
-
break;
|
|
1552
|
-
case 'scroll_to':
|
|
1553
|
-
if (params.index == null) {
|
|
1554
|
-
throw new Error('index parameter is required');
|
|
1555
|
-
}
|
|
1556
|
-
result = await scroll_to_by(context.ekoConfig.chromeProxy, tabId, selector_xpath, params.index);
|
|
1557
|
-
await sleep(500);
|
|
1558
|
-
break;
|
|
1559
|
-
case 'extract_content':
|
|
1560
|
-
let tab = await context.ekoConfig.chromeProxy.tabs.get(tabId);
|
|
1561
|
-
await injectScript(context.ekoConfig.chromeProxy, tabId);
|
|
1562
|
-
await sleep(200);
|
|
1563
|
-
let content = await executeScript(context.ekoConfig.chromeProxy, tabId, () => {
|
|
1564
|
-
return eko.extractHtmlContent();
|
|
1565
|
-
}, []);
|
|
1566
|
-
result = {
|
|
1567
|
-
title: tab.title,
|
|
1568
|
-
url: tab.url,
|
|
1569
|
-
content: content,
|
|
1570
|
-
};
|
|
1571
|
-
break;
|
|
1572
|
-
case 'get_dropdown_options':
|
|
1573
|
-
if (params.index == null) {
|
|
1574
|
-
throw new Error('index parameter is required');
|
|
1575
|
-
}
|
|
1576
|
-
result = await get_dropdown_options(context.ekoConfig.chromeProxy, tabId, selector_xpath, params.index);
|
|
1577
|
-
break;
|
|
1578
|
-
case 'select_dropdown_option':
|
|
1579
|
-
if (params.index == null) {
|
|
1580
|
-
throw new Error('index parameter is required');
|
|
1581
|
-
}
|
|
1582
|
-
if (params.text == null) {
|
|
1583
|
-
throw new Error('text parameter is required');
|
|
1584
|
-
}
|
|
1585
|
-
result = await select_dropdown_option(context.ekoConfig.chromeProxy, tabId, params.text, selector_xpath, params.index);
|
|
1586
|
-
break;
|
|
1587
|
-
case 'screenshot_extract_element':
|
|
1588
|
-
logger.debug("execute 'screenshot_extract_element'...");
|
|
1589
|
-
await sleep(100);
|
|
1590
|
-
logger.debug("injectScript...");
|
|
1591
|
-
await injectScript(context.ekoConfig.chromeProxy, tabId, 'build_dom_tree.js');
|
|
1592
|
-
await sleep(100);
|
|
1593
|
-
try {
|
|
1594
|
-
logger.debug("executeScript...");
|
|
1595
|
-
let element_result = await executeScript(context.ekoConfig.chromeProxy, tabId, () => {
|
|
1596
|
-
return window.get_clickable_elements(true);
|
|
1597
|
-
}, []);
|
|
1598
|
-
context.selector_map = element_result.selector_map;
|
|
1599
|
-
logger.debug("browser.screenshot...");
|
|
1600
|
-
let screenshot$1 = await screenshot(context.ekoConfig.chromeProxy, windowId, true);
|
|
1601
|
-
result = { image: screenshot$1.image, text: element_result.element_str };
|
|
1602
|
-
}
|
|
1603
|
-
finally {
|
|
1604
|
-
await sleep(500);
|
|
1605
|
-
logger.debug("executeScript #2...");
|
|
1606
|
-
await executeScript(context.ekoConfig.chromeProxy, tabId, () => {
|
|
1607
|
-
return window.remove_highlight();
|
|
1608
|
-
}, []);
|
|
1609
|
-
}
|
|
1610
|
-
logger.debug("execute 'screenshot_extract_element'...done");
|
|
1611
|
-
break;
|
|
1612
|
-
default:
|
|
1613
|
-
throw Error(`Invalid parameters. The "${params.action}" value is not included in the "action" enumeration.`);
|
|
1614
|
-
}
|
|
1615
|
-
logger.debug(`execute 'browser_action'...done, result=${result}`);
|
|
1616
|
-
return result;
|
|
1617
|
-
}
|
|
1618
|
-
catch (e) {
|
|
1619
|
-
logger.error('Browser use error:', e);
|
|
1620
|
-
return { success: false, error: e === null || e === void 0 ? void 0 : e.message };
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
destroy(context) {
|
|
1624
|
-
delete context.selector_map;
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
function exportFile(filename, type, content) {
|
|
1629
|
-
const blob = new Blob([content], { type: type });
|
|
1630
|
-
const link = document.createElement('a');
|
|
1631
|
-
link.href = URL.createObjectURL(blob);
|
|
1632
|
-
link.download = filename;
|
|
1633
|
-
document.body.appendChild(link);
|
|
1634
|
-
link.click();
|
|
1635
|
-
document.body.removeChild(link);
|
|
1636
|
-
URL.revokeObjectURL(link.href);
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
/**
|
|
1640
|
-
* Export file
|
|
1641
|
-
*/
|
|
1642
|
-
class ExportFile {
|
|
1643
|
-
constructor() {
|
|
1644
|
-
this.name = 'export_file';
|
|
1645
|
-
this.description = 'Export a text file with content. You should call this tool ONLY when user requires to export a file.';
|
|
1646
|
-
this.input_schema = {
|
|
1647
|
-
type: 'object',
|
|
1648
|
-
properties: {
|
|
1649
|
-
fileType: {
|
|
1650
|
-
type: 'string',
|
|
1651
|
-
description: 'File format type',
|
|
1652
|
-
enum: ['txt', 'csv', 'md', 'html', 'js', 'xml', 'json', 'yml', 'sql'],
|
|
1653
|
-
},
|
|
1654
|
-
content: {
|
|
1655
|
-
type: 'string',
|
|
1656
|
-
description: 'Export file content',
|
|
1657
|
-
},
|
|
1658
|
-
filename: {
|
|
1659
|
-
type: 'string',
|
|
1660
|
-
description: 'File name',
|
|
1661
|
-
},
|
|
1662
|
-
},
|
|
1663
|
-
required: ['fileType', 'content'],
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
/**
|
|
1667
|
-
* export
|
|
1668
|
-
*
|
|
1669
|
-
* @param {*} params { fileType: 'csv', content: 'field1,field2\ndata1,data2' }
|
|
1670
|
-
* @returns > { success: true }
|
|
1671
|
-
*/
|
|
1672
|
-
async execute(context, params) {
|
|
1673
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1674
|
-
if (typeof params !== 'object' || params === null || !('content' in params)) {
|
|
1675
|
-
throw new Error('Invalid parameters. Expected an object with a "content" property.');
|
|
1676
|
-
}
|
|
1677
|
-
await ((_c = (_b = (_a = context.callback) === null || _a === void 0 ? void 0 : _a.hooks) === null || _b === void 0 ? void 0 : _b.onExportFile) === null || _c === void 0 ? void 0 : _c.call(_b, params));
|
|
1678
|
-
let type = 'text/plain';
|
|
1679
|
-
switch (params.fileType) {
|
|
1680
|
-
case 'csv':
|
|
1681
|
-
type = 'text/csv';
|
|
1682
|
-
break;
|
|
1683
|
-
case 'md':
|
|
1684
|
-
type = 'text/markdown';
|
|
1685
|
-
break;
|
|
1686
|
-
case 'html':
|
|
1687
|
-
type = 'text/html';
|
|
1688
|
-
break;
|
|
1689
|
-
case 'js':
|
|
1690
|
-
type = 'application/javascript';
|
|
1691
|
-
break;
|
|
1692
|
-
case 'xml':
|
|
1693
|
-
type = 'text/xml';
|
|
1694
|
-
break;
|
|
1695
|
-
case 'json':
|
|
1696
|
-
type = 'application/json';
|
|
1697
|
-
break;
|
|
1698
|
-
}
|
|
1699
|
-
let filename;
|
|
1700
|
-
if (!params.filename) {
|
|
1701
|
-
filename = new Date().getTime() + '.' + params.fileType;
|
|
1702
|
-
}
|
|
1703
|
-
else if (!(params.filename + '').endsWith(params.fileType)) {
|
|
1704
|
-
filename = params.filename + '.' + params.fileType;
|
|
1705
|
-
}
|
|
1706
|
-
else {
|
|
1707
|
-
filename = params.filename;
|
|
1708
|
-
}
|
|
1709
|
-
try {
|
|
1710
|
-
let tabId = await getTabId(context);
|
|
1711
|
-
await context.ekoConfig.chromeProxy.scripting.executeScript({
|
|
1712
|
-
target: { tabId: tabId },
|
|
1713
|
-
func: exportFile,
|
|
1714
|
-
args: [filename, type, params.content],
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
catch (e) {
|
|
1718
|
-
let tab;
|
|
1719
|
-
const url = 'https://www.google.com';
|
|
1720
|
-
if (context.ekoConfig.workingWindowId) {
|
|
1721
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, context.ekoConfig.workingWindowId);
|
|
1722
|
-
}
|
|
1723
|
-
else {
|
|
1724
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url);
|
|
1725
|
-
}
|
|
1726
|
-
(_f = (_e = (_d = context.callback) === null || _d === void 0 ? void 0 : _d.hooks) === null || _e === void 0 ? void 0 : _e.onTabCreated) === null || _f === void 0 ? void 0 : _f.call(_e, tab.id);
|
|
1727
|
-
let tabId = tab.id;
|
|
1728
|
-
await context.ekoConfig.chromeProxy.scripting.executeScript({
|
|
1729
|
-
target: { tabId: tabId },
|
|
1730
|
-
func: exportFile,
|
|
1731
|
-
args: [filename, type, params.content],
|
|
1732
|
-
});
|
|
1733
|
-
await sleep(5000);
|
|
1734
|
-
await context.ekoConfig.chromeProxy.tabs.remove(tabId);
|
|
1735
|
-
}
|
|
1736
|
-
return { success: true };
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
/**
|
|
1741
|
-
* Extract Page Content
|
|
1742
|
-
*/
|
|
1743
|
-
class ExtractContent {
|
|
1744
|
-
constructor() {
|
|
1745
|
-
this.name = 'extract_content';
|
|
1746
|
-
this.description = 'Extract the complete text content of the current webpage';
|
|
1747
|
-
this.input_schema = {
|
|
1748
|
-
type: 'object',
|
|
1749
|
-
properties: {},
|
|
1750
|
-
};
|
|
1751
|
-
}
|
|
1752
|
-
/**
|
|
1753
|
-
* Extract Page Content
|
|
1754
|
-
*
|
|
1755
|
-
* @param {*} params {}
|
|
1756
|
-
* @returns > { tabId, result: { title, url, content }, success: true }
|
|
1757
|
-
*/
|
|
1758
|
-
async execute(context, params) {
|
|
1759
|
-
let tabId = await getTabId(context);
|
|
1760
|
-
let tab = await context.ekoConfig.chromeProxy.tabs.get(tabId);
|
|
1761
|
-
await injectScript(context.ekoConfig.chromeProxy, tabId);
|
|
1762
|
-
await sleep(500);
|
|
1763
|
-
let content = await executeScript(context.ekoConfig.chromeProxy, tabId, () => {
|
|
1764
|
-
return eko.extractHtmlContent();
|
|
1765
|
-
}, []);
|
|
1766
|
-
return {
|
|
1767
|
-
tabId,
|
|
1768
|
-
result: {
|
|
1769
|
-
title: tab.title,
|
|
1770
|
-
url: tab.url,
|
|
1771
|
-
content: content,
|
|
1772
|
-
}
|
|
1773
|
-
};
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
class GetAllTabs {
|
|
1778
|
-
constructor() {
|
|
1779
|
-
this.name = 'get_all_tabs';
|
|
1780
|
-
this.description = 'Get the tabId, title, url and content from current all tabs without opening new tab.';
|
|
1781
|
-
this.input_schema = {
|
|
1782
|
-
type: 'object',
|
|
1783
|
-
properties: {},
|
|
1784
|
-
};
|
|
1785
|
-
}
|
|
1786
|
-
async execute(context, params) {
|
|
1787
|
-
const currentWindow = await context.ekoConfig.chromeProxy.windows.getCurrent();
|
|
1788
|
-
const windowId = currentWindow.id;
|
|
1789
|
-
const tabs = await context.ekoConfig.chromeProxy.tabs.query({ windowId });
|
|
1790
|
-
const tabsInfo = [];
|
|
1791
|
-
for (const tab of tabs) {
|
|
1792
|
-
if (tab.id === undefined) {
|
|
1793
|
-
logger.warn(`Tab ID is undefined for tab with URL: ${tab.url}`);
|
|
1794
|
-
continue;
|
|
1795
|
-
}
|
|
1796
|
-
await injectScript(context.ekoConfig.chromeProxy, tab.id);
|
|
1797
|
-
await sleep(500);
|
|
1798
|
-
let content = await executeScript(context.ekoConfig.chromeProxy, tab.id, () => {
|
|
1799
|
-
return eko.extractHtmlContent();
|
|
1800
|
-
}, []);
|
|
1801
|
-
// Use title as description, but requirement may evolve
|
|
1802
|
-
let description = tab.title ? tab.title : "No description available.";
|
|
1803
|
-
const tabInfo = {
|
|
1804
|
-
id: tab.id,
|
|
1805
|
-
url: tab.url,
|
|
1806
|
-
title: tab.title,
|
|
1807
|
-
content: content,
|
|
1808
|
-
description: description,
|
|
1809
|
-
};
|
|
1810
|
-
logger.debug(tabInfo);
|
|
1811
|
-
tabsInfo.push(tabInfo);
|
|
1812
|
-
}
|
|
1813
|
-
return tabsInfo;
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
/**
|
|
1818
|
-
* Open Url
|
|
1819
|
-
*/
|
|
1820
|
-
class OpenUrl extends ToolReturnsScreenshot {
|
|
1821
|
-
constructor() {
|
|
1822
|
-
super();
|
|
1823
|
-
this.name = 'open_url';
|
|
1824
|
-
this.description = 'Open the specified URL link in browser window';
|
|
1825
|
-
this.input_schema = {
|
|
1826
|
-
type: 'object',
|
|
1827
|
-
properties: {
|
|
1828
|
-
url: {
|
|
1829
|
-
type: 'string',
|
|
1830
|
-
description: 'URL link address',
|
|
1831
|
-
},
|
|
1832
|
-
newWindow: {
|
|
1833
|
-
type: 'boolean',
|
|
1834
|
-
description: 'true: Open in a new window; false: Open in the current window.',
|
|
1835
|
-
},
|
|
1836
|
-
},
|
|
1837
|
-
required: ['url'],
|
|
1838
|
-
};
|
|
1839
|
-
}
|
|
1840
|
-
/**
|
|
1841
|
-
* Open Url
|
|
1842
|
-
*
|
|
1843
|
-
* @param {*} params { url: 'https://www.google.com', newWindow: true }
|
|
1844
|
-
* @returns > { tabId, windowId, title, success: true }
|
|
1845
|
-
*/
|
|
1846
|
-
async realExecute(context, params) {
|
|
1847
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1848
|
-
// 参数验证
|
|
1849
|
-
if (typeof params !== 'object' || params === null || !params.url) {
|
|
1850
|
-
logger.error('Invalid parameters. Expected an object with a "url" property.');
|
|
1851
|
-
throw new Error('Invalid parameters. Expected an object with a "url" property.');
|
|
1852
|
-
}
|
|
1853
|
-
// 提取参数
|
|
1854
|
-
let url = params.url.trim();
|
|
1855
|
-
let newWindow = params.newWindow;
|
|
1856
|
-
logger.debug('URL to open:', url);
|
|
1857
|
-
logger.debug('Initial newWindow value:', newWindow);
|
|
1858
|
-
// 根据上下文调整 newWindow 的值
|
|
1859
|
-
if (context.ekoConfig.workingWindowId) {
|
|
1860
|
-
logger.debug('Working window ID exists in context, setting newWindow to false.');
|
|
1861
|
-
newWindow = false;
|
|
1862
|
-
}
|
|
1863
|
-
else if (!newWindow && !context.variables.get('windowId') && !context.variables.get('tabId')) {
|
|
1864
|
-
// First mandatory opening of a new window
|
|
1865
|
-
logger.debug('No existing window or tab ID found, forcing newWindow to true.');
|
|
1866
|
-
newWindow = true;
|
|
1867
|
-
}
|
|
1868
|
-
logger.debug('Final newWindow value:', newWindow);
|
|
1869
|
-
// 打开新标签页
|
|
1870
|
-
let tab;
|
|
1871
|
-
if (newWindow) {
|
|
1872
|
-
logger.debug('Opening new tab in a new window.');
|
|
1873
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url);
|
|
1874
|
-
(_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);
|
|
1875
|
-
logger.debug('New tab created in a new window:', tab.id);
|
|
1876
|
-
}
|
|
1877
|
-
else {
|
|
1878
|
-
let windowId = context.ekoConfig.workingWindowId ? context.ekoConfig.workingWindowId : await getWindowId(context);
|
|
1879
|
-
logger.debug('Using existing window with ID:', windowId);
|
|
1880
|
-
try {
|
|
1881
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, windowId);
|
|
1882
|
-
logger.debug("Calling hook...");
|
|
1883
|
-
(_f = (_e = (_d = context.callback) === null || _d === void 0 ? void 0 : _d.hooks) === null || _e === void 0 ? void 0 : _e.onTabCreated) === null || _f === void 0 ? void 0 : _f.call(_e, tab.id);
|
|
1884
|
-
logger.debug('New tab created in existing window:', tab.id);
|
|
1885
|
-
}
|
|
1886
|
-
catch (e) {
|
|
1887
|
-
logger.error("An error occurs when `open_url`", e);
|
|
1888
|
-
throw e;
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
// 获取窗口和标签 ID
|
|
1892
|
-
let windowId = tab.windowId;
|
|
1893
|
-
let tabId = tab.id;
|
|
1894
|
-
logger.debug('Tab ID:', tabId, 'Window ID:', windowId);
|
|
1895
|
-
// 更新上下文变量
|
|
1896
|
-
context.variables.set('windowId', windowId);
|
|
1897
|
-
context.variables.set('tabId', tabId);
|
|
1898
|
-
logger.debug('Updated context variables:', context.variables);
|
|
1899
|
-
// 处理新窗口的 windowIds
|
|
1900
|
-
if (newWindow) {
|
|
1901
|
-
let windowIds = context.variables.get('windowIds');
|
|
1902
|
-
if (windowIds) {
|
|
1903
|
-
logger.debug('Existing window IDs:', windowIds);
|
|
1904
|
-
windowIds.push(windowId);
|
|
1905
|
-
logger.debug('Updated window IDs:', windowIds);
|
|
1906
|
-
}
|
|
1907
|
-
else {
|
|
1908
|
-
logger.debug('No existing window IDs found, creating new array.');
|
|
1909
|
-
context.variables.set('windowIds', [windowId]);
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
// 返回结果
|
|
1913
|
-
let result = {
|
|
1914
|
-
tabId,
|
|
1915
|
-
windowId,
|
|
1916
|
-
title: tab.title,
|
|
1917
|
-
};
|
|
1918
|
-
logger.debug('Returning result:', result);
|
|
1919
|
-
return result;
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
/**
|
|
1924
|
-
* Current Page Screenshot
|
|
1925
|
-
*/
|
|
1926
|
-
class Screenshot {
|
|
1927
|
-
constructor() {
|
|
1928
|
-
this.name = 'screenshot';
|
|
1929
|
-
this.description = 'Screenshot the current webpage window';
|
|
1930
|
-
this.input_schema = {
|
|
1931
|
-
type: 'object',
|
|
1932
|
-
properties: {},
|
|
1933
|
-
};
|
|
1934
|
-
}
|
|
1935
|
-
/**
|
|
1936
|
-
* Current Page Screenshot
|
|
1937
|
-
*
|
|
1938
|
-
* @param {*} params {}
|
|
1939
|
-
* @returns > { image: { type: 'base64', media_type: 'image/png', data } }
|
|
1940
|
-
*/
|
|
1941
|
-
async execute(context, params) {
|
|
1942
|
-
let windowId = await getWindowId(context);
|
|
1943
|
-
return await screenshot(context.ekoConfig.chromeProxy, windowId);
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
/**
|
|
1948
|
-
* Browser tab management
|
|
1949
|
-
*/
|
|
1950
|
-
class TabManagement {
|
|
1951
|
-
constructor() {
|
|
1952
|
-
this.name = 'tab_management';
|
|
1953
|
-
this.description = 'Browser tab management, view and operate tabs.You can use this tool to' +
|
|
1954
|
-
'View all tabs with the tabId and title.Get current tab information (tabId, url, title).' +
|
|
1955
|
-
'Go back to the previous page in the current tab. And Close the current tab.';
|
|
1956
|
-
this.input_schema = {
|
|
1957
|
-
type: 'object',
|
|
1958
|
-
properties: {
|
|
1959
|
-
command: {
|
|
1960
|
-
type: 'string',
|
|
1961
|
-
description: `The command to perform. The available commands are:
|
|
1962
|
-
* \`tab_all\`: View all tabs and return the tabId and title.
|
|
1963
|
-
* \`go_back\`: Go back to the previous page in the current tab.
|
|
1964
|
-
* \`switch_tab\`: Switch to the specified tab by tabId.`,
|
|
1965
|
-
enum: ['tab_all', 'go_back', 'switch_tab'],
|
|
1966
|
-
},
|
|
1967
|
-
tabId: {
|
|
1968
|
-
type: 'integer',
|
|
1969
|
-
description: "Tab id. Only needed when using 'switch_tab'",
|
|
1970
|
-
},
|
|
1971
|
-
},
|
|
1972
|
-
required: ['command'],
|
|
1973
|
-
};
|
|
1974
|
-
}
|
|
1975
|
-
async execute(context, params) {
|
|
1976
|
-
if (params === null || !params.command) {
|
|
1977
|
-
throw new Error('Invalid parameters. Expected an object with a "command" property.');
|
|
1978
|
-
}
|
|
1979
|
-
if (params.command == 'tab_all') ;
|
|
1980
|
-
else if (params.command == 'go_back') {
|
|
1981
|
-
let tabId = await getTabId(context);
|
|
1982
|
-
await context.ekoConfig.chromeProxy.tabs.goBack(tabId);
|
|
1983
|
-
}
|
|
1984
|
-
else if (params.command == "switch_tab") {
|
|
1985
|
-
await context.ekoConfig.chromeProxy.tabs.select(params.tabId);
|
|
1986
|
-
}
|
|
1987
|
-
else if (params.command == 'close_tab') {
|
|
1988
|
-
let closedTabId = await getTabId(context);
|
|
1989
|
-
await context.ekoConfig.chromeProxy.tabs.remove(closedTabId);
|
|
1990
|
-
await sleep(100);
|
|
1991
|
-
let tabs = await context.ekoConfig.chromeProxy.tabs.query({ active: true, currentWindow: true });
|
|
1992
|
-
if (tabs.length == 0) {
|
|
1993
|
-
tabs = await context.ekoConfig.chromeProxy.tabs.query({ status: 'complete', currentWindow: true });
|
|
1994
|
-
}
|
|
1995
|
-
let tab = tabs[tabs.length - 1];
|
|
1996
|
-
if (!tab.active) {
|
|
1997
|
-
await context.ekoConfig.chromeProxy.tabs.update(tab.id, { active: true });
|
|
1998
|
-
}
|
|
1999
|
-
context.variables.set('tabId', tab.id);
|
|
2000
|
-
context.variables.set('windowId', tab.windowId);
|
|
2001
|
-
}
|
|
2002
|
-
else {
|
|
2003
|
-
throw Error('Unknown command: ' + params.command);
|
|
2004
|
-
}
|
|
2005
|
-
// build return value
|
|
2006
|
-
let tabs = await context.ekoConfig.chromeProxy.tabs.query({});
|
|
2007
|
-
tabs = tabs.filter((tab) => tab.title && tab.url);
|
|
2008
|
-
if (tabs.length > 0) {
|
|
2009
|
-
let result = "After operation, the existing tabs are as follows:\n";
|
|
2010
|
-
for (const tab of tabs) {
|
|
2011
|
-
result += `<tab><id>${tab.id}</id><title>${tab.title}</title><url>${tab.url}</url></tab>\n`;
|
|
2012
|
-
}
|
|
2013
|
-
let currentTabId = await getTabId(context);
|
|
2014
|
-
let currentTab = await context.ekoConfig.chromeProxy.tabs.get(currentTabId);
|
|
2015
|
-
result += `The current active tab: <tab><id>${currentTab.id}</id><title>${currentTab.title}</title><url>${currentTab.url}</url></tab>`;
|
|
2016
|
-
return result;
|
|
2017
|
-
}
|
|
2018
|
-
else {
|
|
2019
|
-
return "No existing tab. Use 'open_url' to open a new tab";
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
destroy(context) {
|
|
2023
|
-
let windowIds = context.variables.get('windowIds');
|
|
2024
|
-
if (windowIds) {
|
|
2025
|
-
for (let i = 0; i < windowIds.length; i++) {
|
|
2026
|
-
context.ekoConfig.chromeProxy.windows.remove(windowIds[i]);
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
/**
|
|
2033
|
-
* Web Search
|
|
2034
|
-
*/
|
|
2035
|
-
class WebSearch {
|
|
2036
|
-
constructor() {
|
|
2037
|
-
this.name = 'web_search';
|
|
2038
|
-
this.description = 'Search the web based on keywords and return relevant extracted content from webpages.';
|
|
2039
|
-
this.input_schema = {
|
|
2040
|
-
type: 'object',
|
|
2041
|
-
properties: {
|
|
2042
|
-
url: {
|
|
2043
|
-
type: 'string',
|
|
2044
|
-
description: 'the URL of search engine, like https://www.bing.com'
|
|
2045
|
-
},
|
|
2046
|
-
query: {
|
|
2047
|
-
type: 'string',
|
|
2048
|
-
description: 'search for keywords',
|
|
2049
|
-
},
|
|
2050
|
-
maxResults: {
|
|
2051
|
-
type: 'integer',
|
|
2052
|
-
description: 'Maximum search results, default 5',
|
|
2053
|
-
},
|
|
2054
|
-
},
|
|
2055
|
-
required: ['query'],
|
|
2056
|
-
};
|
|
2057
|
-
}
|
|
2058
|
-
/**
|
|
2059
|
-
* search
|
|
2060
|
-
*
|
|
2061
|
-
* @param {*} params { url: 'https://www.google.com', query: 'ai agent', maxResults: 5 }
|
|
2062
|
-
* @returns > [{ title, url, content }]
|
|
2063
|
-
*/
|
|
2064
|
-
async execute(context, params) {
|
|
2065
|
-
var _a, _b;
|
|
2066
|
-
if (typeof params !== 'object' || params === null || !params.query) {
|
|
2067
|
-
throw new Error('Invalid parameters. Expected an object with a "query" property.');
|
|
2068
|
-
}
|
|
2069
|
-
let url = (_a = params.url) === null || _a === void 0 ? void 0 : _a.trim();
|
|
2070
|
-
let query = params.query;
|
|
2071
|
-
let maxResults = params.maxResults;
|
|
2072
|
-
if (!url) {
|
|
2073
|
-
url = 'https://www.bing.com';
|
|
2074
|
-
}
|
|
2075
|
-
let taskId = new Date().getTime() + '';
|
|
2076
|
-
let searchs = [{ url: url, keyword: query }];
|
|
2077
|
-
let searchInfo = await deepSearch(context, taskId, searchs, maxResults || 5, context.ekoConfig.workingWindowId);
|
|
2078
|
-
let links = ((_b = searchInfo.result[0]) === null || _b === void 0 ? void 0 : _b.links) || [];
|
|
2079
|
-
return links.filter((s) => s.content.slice(0, 8000));
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
const deepSearchInjects = {
|
|
2083
|
-
'bing.com': {
|
|
2084
|
-
filename: 'bing.js',
|
|
2085
|
-
buildSearchUrl: function (url, keyword) {
|
|
2086
|
-
return 'https://bing.com/search?q=' + encodeURI(keyword);
|
|
2087
|
-
},
|
|
2088
|
-
},
|
|
2089
|
-
'duckduckgo.com': {
|
|
2090
|
-
filename: 'duckduckgo.js',
|
|
2091
|
-
buildSearchUrl: function (url, keyword) {
|
|
2092
|
-
return 'https://duckduckgo.com/?q=' + encodeURI(keyword);
|
|
2093
|
-
},
|
|
2094
|
-
},
|
|
2095
|
-
'google.com': {
|
|
2096
|
-
filename: 'google.js',
|
|
2097
|
-
buildSearchUrl: function (url, keyword) {
|
|
2098
|
-
return 'https://www.google.com/search?q=' + encodeURI(keyword);
|
|
2099
|
-
},
|
|
2100
|
-
},
|
|
2101
|
-
default: {
|
|
2102
|
-
filename: 'google.js',
|
|
2103
|
-
buildSearchUrl: function (url, keyword) {
|
|
2104
|
-
url = url.trim();
|
|
2105
|
-
let idx = url.indexOf('//');
|
|
2106
|
-
if (idx > -1) {
|
|
2107
|
-
url = url.substring(idx + 2);
|
|
2108
|
-
}
|
|
2109
|
-
idx = url.indexOf('/', 2);
|
|
2110
|
-
if (idx > -1) {
|
|
2111
|
-
url = url.substring(0, idx);
|
|
2112
|
-
}
|
|
2113
|
-
keyword = 'site:' + url + ' ' + keyword;
|
|
2114
|
-
return 'https://www.google.com/search?q=' + encodeURIComponent(keyword);
|
|
2115
|
-
},
|
|
2116
|
-
},
|
|
2117
|
-
};
|
|
2118
|
-
function buildDeepSearchUrl(url, keyword) {
|
|
2119
|
-
let idx = url.indexOf('/', url.indexOf('//') + 2);
|
|
2120
|
-
let baseUrl = idx > -1 ? url.substring(0, idx) : url;
|
|
2121
|
-
let domains = Object.keys(deepSearchInjects);
|
|
2122
|
-
let inject = null;
|
|
2123
|
-
for (let j = 0; j < domains.length; j++) {
|
|
2124
|
-
let domain = domains[j];
|
|
2125
|
-
if (baseUrl == domain || baseUrl.endsWith('.' + domain) || baseUrl.endsWith('/' + domain)) {
|
|
2126
|
-
inject = deepSearchInjects[domain];
|
|
2127
|
-
break;
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
if (!inject) {
|
|
2131
|
-
inject = deepSearchInjects['default'];
|
|
2132
|
-
}
|
|
2133
|
-
return {
|
|
2134
|
-
filename: inject.filename,
|
|
2135
|
-
url: inject.buildSearchUrl(url, keyword),
|
|
2136
|
-
};
|
|
2137
|
-
}
|
|
2138
|
-
// Event
|
|
2139
|
-
const tabsUpdateEvent = new MsgEvent();
|
|
2140
|
-
// TODO: replace `chrome` with `context.ekoConfig.chromeProxy`
|
|
2141
|
-
if (typeof chrome !== 'undefined' && typeof chrome.tabs !== 'undefined') {
|
|
2142
|
-
chrome.tabs.onUpdated.addListener(async function (tabId, changeInfo, tab) {
|
|
2143
|
-
await tabsUpdateEvent.publish({ tabId, changeInfo, tab });
|
|
2144
|
-
});
|
|
2145
|
-
}
|
|
2146
|
-
/**
|
|
2147
|
-
* deep search
|
|
2148
|
-
*
|
|
2149
|
-
* @param {string} taskId task id
|
|
2150
|
-
* @param {array} searchs search list => [{ url: 'https://bing.com', keyword: 'ai' }]
|
|
2151
|
-
* @param {number} detailsMaxNum Maximum crawling quantity per search detail page
|
|
2152
|
-
*/
|
|
2153
|
-
async function deepSearch(context, taskId, searchs, detailsMaxNum, windowId) {
|
|
2154
|
-
let closeWindow = false;
|
|
2155
|
-
if (!windowId) {
|
|
2156
|
-
// open new window
|
|
2157
|
-
let window = await context.ekoConfig.chromeProxy.windows.create({
|
|
2158
|
-
type: 'normal',
|
|
2159
|
-
state: 'maximized',
|
|
2160
|
-
url: null,
|
|
2161
|
-
});
|
|
2162
|
-
windowId = window.id;
|
|
2163
|
-
closeWindow = true;
|
|
2164
|
-
}
|
|
2165
|
-
windowId = windowId;
|
|
2166
|
-
// crawler the search page details page link
|
|
2167
|
-
// [{ links: [{ title, url }] }]
|
|
2168
|
-
let detailLinkGroups = await doDetailLinkGroups(context, taskId, searchs, detailsMaxNum, windowId);
|
|
2169
|
-
// crawler all details page content and comments
|
|
2170
|
-
let searchInfo = await doPageContent(context, taskId, detailLinkGroups, windowId);
|
|
2171
|
-
logger.debug('searchInfo: ', searchInfo);
|
|
2172
|
-
// close window
|
|
2173
|
-
closeWindow && context.ekoConfig.chromeProxy.windows.remove(windowId);
|
|
2174
|
-
return searchInfo;
|
|
2175
|
-
}
|
|
2176
|
-
/**
|
|
2177
|
-
* crawler the search page details page link
|
|
2178
|
-
*
|
|
2179
|
-
* @param {string} taskId task id
|
|
2180
|
-
* @param {array} searchs search list => [{ url: 'https://bing.com', keyword: 'ai' }]
|
|
2181
|
-
* @param {number} detailsMaxNum Maximum crawling quantity per search detail page
|
|
2182
|
-
* @param {*} window
|
|
2183
|
-
* @returns [{ links: [{ title, url }] }]
|
|
2184
|
-
*/
|
|
2185
|
-
async function doDetailLinkGroups(context, taskId, searchs, detailsMaxNum, windowId) {
|
|
2186
|
-
var _a, _b, _c;
|
|
2187
|
-
let detailLinkGroups = [];
|
|
2188
|
-
let countDownLatch = new CountDownLatch(searchs.length);
|
|
2189
|
-
for (let i = 0; i < searchs.length; i++) {
|
|
2190
|
-
try {
|
|
2191
|
-
// script name & build search URL
|
|
2192
|
-
const { filename, url } = buildDeepSearchUrl(searchs[i].url, searchs[i].keyword);
|
|
2193
|
-
// open new Tab
|
|
2194
|
-
let tab = await context.ekoConfig.chromeProxy.tabs.create({
|
|
2195
|
-
url: url,
|
|
2196
|
-
windowId,
|
|
2197
|
-
});
|
|
2198
|
-
(_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);
|
|
2199
|
-
let eventId = taskId + '_' + i;
|
|
2200
|
-
// monitor Tab status
|
|
2201
|
-
tabsUpdateEvent.addListener(async function (obj) {
|
|
2202
|
-
if (obj.tabId != tab.id) {
|
|
2203
|
-
return;
|
|
2204
|
-
}
|
|
2205
|
-
if (obj.changeInfo.status === 'complete') {
|
|
2206
|
-
tabsUpdateEvent.removeListener(eventId);
|
|
2207
|
-
// inject js
|
|
2208
|
-
await injectScript(context.ekoConfig.chromeProxy, tab.id, filename);
|
|
2209
|
-
await sleep(1000);
|
|
2210
|
-
// crawler the search page details page
|
|
2211
|
-
// { links: [{ title, url }] }
|
|
2212
|
-
let detailLinks = await context.ekoConfig.chromeProxy.tabs.sendMessage(tab.id, {
|
|
2213
|
-
type: 'page:getDetailLinks',
|
|
2214
|
-
keyword: searchs[i].keyword,
|
|
2215
|
-
});
|
|
2216
|
-
logger.debug('detailLinks: ', detailLinks);
|
|
2217
|
-
if (!detailLinks || !detailLinks.links) {
|
|
2218
|
-
logger.error("detailLinks is empty");
|
|
2219
|
-
throw new Error("An error occurs when calling `web_search`, please try again.");
|
|
2220
|
-
}
|
|
2221
|
-
let links = detailLinks.links.slice(0, detailsMaxNum);
|
|
2222
|
-
detailLinkGroups.push({ url, links, filename });
|
|
2223
|
-
countDownLatch.countDown();
|
|
2224
|
-
context.ekoConfig.chromeProxy.tabs.remove(tab.id);
|
|
2225
|
-
}
|
|
2226
|
-
else if (obj.changeInfo.status === 'unloaded') {
|
|
2227
|
-
countDownLatch.countDown();
|
|
2228
|
-
context.ekoConfig.chromeProxy.tabs.remove(tab.id);
|
|
2229
|
-
tabsUpdateEvent.removeListener(eventId);
|
|
2230
|
-
}
|
|
2231
|
-
}, eventId);
|
|
2232
|
-
}
|
|
2233
|
-
catch (e) {
|
|
2234
|
-
logger.error(e);
|
|
2235
|
-
countDownLatch.countDown();
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
await countDownLatch.await(30000);
|
|
2239
|
-
return detailLinkGroups;
|
|
2240
|
-
}
|
|
2241
|
-
/**
|
|
2242
|
-
* page content
|
|
2243
|
-
*
|
|
2244
|
-
* @param {string} taskId task id
|
|
2245
|
-
* @param {array} detailLinkGroups details page group
|
|
2246
|
-
* @param {*} window
|
|
2247
|
-
* @returns search info
|
|
2248
|
-
*/
|
|
2249
|
-
async function doPageContent(context, taskId, detailLinkGroups, windowId) {
|
|
2250
|
-
var _a, _b, _c;
|
|
2251
|
-
const searchInfo = {
|
|
2252
|
-
total: 0,
|
|
2253
|
-
running: 0,
|
|
2254
|
-
succeed: 0,
|
|
2255
|
-
failed: 0,
|
|
2256
|
-
failedLinks: [],
|
|
2257
|
-
result: detailLinkGroups,
|
|
2258
|
-
};
|
|
2259
|
-
for (let i = 0; i < detailLinkGroups.length; i++) {
|
|
2260
|
-
let links = detailLinkGroups[i].links;
|
|
2261
|
-
searchInfo.total += links.length;
|
|
2262
|
-
}
|
|
2263
|
-
let countDownLatch = new CountDownLatch(searchInfo.total);
|
|
2264
|
-
for (let i = 0; i < detailLinkGroups.length; i++) {
|
|
2265
|
-
let filename = detailLinkGroups[i].filename;
|
|
2266
|
-
let links = detailLinkGroups[i].links;
|
|
2267
|
-
for (let j = 0; j < links.length; j++) {
|
|
2268
|
-
let link = links[j];
|
|
2269
|
-
// open new tab
|
|
2270
|
-
let tab = await context.ekoConfig.chromeProxy.tabs.create({
|
|
2271
|
-
url: link.url,
|
|
2272
|
-
windowId,
|
|
2273
|
-
});
|
|
2274
|
-
(_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);
|
|
2275
|
-
searchInfo.running++;
|
|
2276
|
-
let eventId = taskId + '_' + i + '_' + j;
|
|
2277
|
-
// Create a timeout promise
|
|
2278
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
2279
|
-
setTimeout(() => reject(new Error('Page load timeout')), 10000); // Timeout after 10 seconds
|
|
2280
|
-
});
|
|
2281
|
-
// Create a tab monitoring promise
|
|
2282
|
-
const monitorTabPromise = new Promise(async (resolve, reject) => {
|
|
2283
|
-
tabsUpdateEvent.addListener(async function onTabUpdated(obj) {
|
|
2284
|
-
if (obj.tabId !== tab.id)
|
|
2285
|
-
return;
|
|
2286
|
-
if (obj.changeInfo.status === 'complete') {
|
|
2287
|
-
tabsUpdateEvent.removeListener(eventId);
|
|
2288
|
-
try {
|
|
2289
|
-
// Inject script and get page content
|
|
2290
|
-
await injectScript(context.ekoConfig.chromeProxy, tab.id, filename);
|
|
2291
|
-
await sleep(1000);
|
|
2292
|
-
let result = await context.ekoConfig.chromeProxy.tabs.sendMessage(tab.id, {
|
|
2293
|
-
type: 'page:getContent',
|
|
2294
|
-
});
|
|
2295
|
-
if (!result)
|
|
2296
|
-
throw new Error('No Result');
|
|
2297
|
-
link.content = result.content;
|
|
2298
|
-
link.page_title = result.title;
|
|
2299
|
-
searchInfo.succeed++;
|
|
2300
|
-
resolve(); // Resolve the promise if successful
|
|
2301
|
-
}
|
|
2302
|
-
catch (error) {
|
|
2303
|
-
searchInfo.failed++;
|
|
2304
|
-
searchInfo.failedLinks.push(link);
|
|
2305
|
-
reject(error); // Reject the promise on error
|
|
2306
|
-
}
|
|
2307
|
-
finally {
|
|
2308
|
-
searchInfo.running--;
|
|
2309
|
-
countDownLatch.countDown();
|
|
2310
|
-
context.ekoConfig.chromeProxy.tabs.remove(tab.id);
|
|
2311
|
-
tabsUpdateEvent.removeListener(eventId);
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
else if (obj.changeInfo.status === 'unloaded') {
|
|
2315
|
-
searchInfo.running--;
|
|
2316
|
-
countDownLatch.countDown();
|
|
2317
|
-
context.ekoConfig.chromeProxy.tabs.remove(tab.id);
|
|
2318
|
-
tabsUpdateEvent.removeListener(eventId);
|
|
2319
|
-
reject(new Error('Tab unloaded')); // Reject if the tab is unloaded
|
|
2320
|
-
}
|
|
2321
|
-
}, eventId);
|
|
2322
|
-
});
|
|
2323
|
-
// Use Promise.race to enforce the timeout
|
|
2324
|
-
try {
|
|
2325
|
-
await Promise.race([monitorTabPromise, timeoutPromise]);
|
|
2326
|
-
}
|
|
2327
|
-
catch (e) {
|
|
2328
|
-
logger.error(`${link.title} failed:`, e);
|
|
2329
|
-
searchInfo.running--;
|
|
2330
|
-
searchInfo.failed++;
|
|
2331
|
-
searchInfo.failedLinks.push(link);
|
|
2332
|
-
countDownLatch.countDown();
|
|
2333
|
-
context.ekoConfig.chromeProxy.tabs.remove(tab.id); // Clean up tab on failure
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
await countDownLatch.await(60000);
|
|
2338
|
-
return searchInfo;
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
class RequestLogin {
|
|
2342
|
-
constructor() {
|
|
2343
|
-
this.name = 'request_login';
|
|
2344
|
-
this.description =
|
|
2345
|
-
'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.';
|
|
2346
|
-
this.input_schema = {
|
|
2347
|
-
type: 'object',
|
|
2348
|
-
properties: {},
|
|
2349
|
-
};
|
|
2350
|
-
}
|
|
2351
|
-
async execute(context, params) {
|
|
2352
|
-
if (!params.force && await this.isLoginIn(context)) {
|
|
2353
|
-
return true;
|
|
2354
|
-
}
|
|
2355
|
-
let tabId = await getTabId(context);
|
|
2356
|
-
let task_id = 'login_required_' + tabId;
|
|
2357
|
-
const request_user_help = async () => {
|
|
2358
|
-
await context.ekoConfig.chromeProxy.tabs.sendMessage(tabId, {
|
|
2359
|
-
type: 'request_user_help',
|
|
2360
|
-
task_id,
|
|
2361
|
-
failure_type: 'login_required',
|
|
2362
|
-
failure_message: 'Access page require user authentication.',
|
|
2363
|
-
});
|
|
2364
|
-
};
|
|
2365
|
-
const login_interval = setInterval(async () => {
|
|
2366
|
-
try {
|
|
2367
|
-
request_user_help();
|
|
2368
|
-
}
|
|
2369
|
-
catch (e) {
|
|
2370
|
-
clearInterval(login_interval);
|
|
2371
|
-
}
|
|
2372
|
-
}, 2000);
|
|
2373
|
-
try {
|
|
2374
|
-
return await this.awaitLogin(context.ekoConfig.chromeProxy, tabId, task_id);
|
|
2375
|
-
}
|
|
2376
|
-
finally {
|
|
2377
|
-
clearInterval(login_interval);
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
async awaitLogin(chromeProxy, tabId, task_id) {
|
|
2381
|
-
return new Promise((resolve) => {
|
|
2382
|
-
const checkTabClosedInterval = setInterval(async () => {
|
|
2383
|
-
const tabExists = await doesTabExists(chromeProxy, tabId);
|
|
2384
|
-
if (!tabExists) {
|
|
2385
|
-
clearInterval(checkTabClosedInterval);
|
|
2386
|
-
resolve(false);
|
|
2387
|
-
chromeProxy.runtime.onMessage.removeListener(listener);
|
|
2388
|
-
}
|
|
2389
|
-
}, 1000);
|
|
2390
|
-
const listener = (message) => {
|
|
2391
|
-
if (message.type === 'issue_resolved' && message.task_id === task_id) {
|
|
2392
|
-
resolve(true);
|
|
2393
|
-
clearInterval(checkTabClosedInterval);
|
|
2394
|
-
}
|
|
2395
|
-
};
|
|
2396
|
-
chromeProxy.runtime.onMessage.addListener(listener);
|
|
2397
|
-
});
|
|
2398
|
-
}
|
|
2399
|
-
async isLoginIn(context) {
|
|
2400
|
-
let windowId = await getWindowId(context);
|
|
2401
|
-
let screenshot_result = await screenshot(context.ekoConfig.chromeProxy, windowId, true);
|
|
2402
|
-
let messages = [
|
|
2403
|
-
{
|
|
2404
|
-
role: 'user',
|
|
2405
|
-
content: [
|
|
2406
|
-
{
|
|
2407
|
-
type: 'image',
|
|
2408
|
-
source: screenshot_result.image,
|
|
2409
|
-
},
|
|
2410
|
-
{
|
|
2411
|
-
type: 'text',
|
|
2412
|
-
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.',
|
|
2413
|
-
},
|
|
2414
|
-
],
|
|
2415
|
-
},
|
|
2416
|
-
];
|
|
2417
|
-
let response = await context.llmProvider.generateText(messages, { maxTokens: 256 });
|
|
2418
|
-
let text = response.textContent;
|
|
2419
|
-
if (!text) {
|
|
2420
|
-
text = JSON.stringify(response.content);
|
|
2421
|
-
}
|
|
2422
|
-
return text.indexOf('LOGGED_IN') > -1;
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
|
-
class SwitchTab {
|
|
2427
|
-
constructor() {
|
|
2428
|
-
this.name = 'switch_tab';
|
|
2429
|
-
this.description = 'Switch to the specified tab using tabId';
|
|
2430
|
-
this.input_schema = {
|
|
2431
|
-
type: 'object',
|
|
2432
|
-
properties: {
|
|
2433
|
-
tabId: {
|
|
2434
|
-
type: 'integer',
|
|
2435
|
-
description: 'The tabId to switch to',
|
|
2436
|
-
},
|
|
2437
|
-
},
|
|
2438
|
-
required: ['tabId'],
|
|
2439
|
-
};
|
|
2440
|
-
}
|
|
2441
|
-
async execute(context, params) {
|
|
2442
|
-
if (params === null || !params.tabId) {
|
|
2443
|
-
throw new Error('Invalid parameters. Expected an object with a "tabId" property.');
|
|
2444
|
-
}
|
|
2445
|
-
let result;
|
|
2446
|
-
let tabId = parseInt(String(params.tabId));
|
|
2447
|
-
let tab = await context.ekoConfig.chromeProxy.tabs.update(tabId, { active: true });
|
|
2448
|
-
context.variables.set('tabId', tab.id);
|
|
2449
|
-
context.variables.set('windowId', tab.windowId);
|
|
2450
|
-
let tabInfo = { tabId, windowId: tab.windowId, title: tab.title, url: tab.url };
|
|
2451
|
-
result = tabInfo;
|
|
2452
|
-
return result;
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
class CancelWorkflow {
|
|
2457
|
-
constructor() {
|
|
2458
|
-
this.name = 'cancel_workflow';
|
|
2459
|
-
this.description = 'Cancel the workflow when encountering critical errors that cannot be resolved through user interaction or retry. This should only be used when the workflow is in an unrecoverable state. ';
|
|
2460
|
-
this.input_schema = {
|
|
2461
|
-
type: 'object',
|
|
2462
|
-
properties: {
|
|
2463
|
-
reason: {
|
|
2464
|
-
type: 'string',
|
|
2465
|
-
description: 'Why the workflow should be cancelled.',
|
|
2466
|
-
},
|
|
2467
|
-
},
|
|
2468
|
-
required: ['reason'],
|
|
2469
|
-
};
|
|
2470
|
-
}
|
|
2471
|
-
async execute(context, params) {
|
|
2472
|
-
var _a;
|
|
2473
|
-
if (typeof params !== 'object' || params === null || !params.reason) {
|
|
2474
|
-
throw new Error('Invalid parameters. Expected an object with a "reason" property.');
|
|
2475
|
-
}
|
|
2476
|
-
const reason = params.reason;
|
|
2477
|
-
logger.info("The workflow has been cancelled because: " + reason);
|
|
2478
|
-
await ((_a = context.workflow) === null || _a === void 0 ? void 0 : _a.cancel());
|
|
2479
|
-
return;
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
|
-
|
|
2483
|
-
class HumanInputText {
|
|
2484
|
-
constructor() {
|
|
2485
|
-
this.name = 'human_input_text';
|
|
2486
|
-
this.description = 'When you are unsure about the details of your next action or need the user to perform a local action, call me and ask the user for details in the "question" field. The user will provide you with a text as an answer.';
|
|
2487
|
-
this.input_schema = {
|
|
2488
|
-
type: 'object',
|
|
2489
|
-
properties: {
|
|
2490
|
-
question: {
|
|
2491
|
-
type: 'string',
|
|
2492
|
-
description: 'Ask the user here. Should follow the format: "Please input ...".',
|
|
2493
|
-
},
|
|
2494
|
-
},
|
|
2495
|
-
required: ['question'],
|
|
2496
|
-
};
|
|
2497
|
-
}
|
|
2498
|
-
async execute(context, params) {
|
|
2499
|
-
var _a;
|
|
2500
|
-
if (typeof params !== 'object' || params === null || !params.question) {
|
|
2501
|
-
throw new Error('Invalid parameters. Expected an object with a "question" property.');
|
|
2502
|
-
}
|
|
2503
|
-
const question = params.question;
|
|
2504
|
-
logger.debug("question: " + question);
|
|
2505
|
-
let onHumanInputText = (_a = context.callback) === null || _a === void 0 ? void 0 : _a.hooks.onHumanInputText;
|
|
2506
|
-
if (onHumanInputText) {
|
|
2507
|
-
let answer;
|
|
2508
|
-
try {
|
|
2509
|
-
answer = await onHumanInputText(question);
|
|
2510
|
-
}
|
|
2511
|
-
catch (e) {
|
|
2512
|
-
logger.warn(e);
|
|
2513
|
-
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2514
|
-
}
|
|
2515
|
-
logger.debug("answer: " + answer);
|
|
2516
|
-
return { status: "OK", answer: answer };
|
|
2517
|
-
}
|
|
2518
|
-
else {
|
|
2519
|
-
logger.error("`onHumanInputText` not implemented");
|
|
2520
|
-
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
|
-
class HumanInputSingleChoice {
|
|
2525
|
-
constructor() {
|
|
2526
|
-
this.name = 'human_input_single_choice';
|
|
2527
|
-
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.';
|
|
2528
|
-
this.input_schema = {
|
|
2529
|
-
type: 'object',
|
|
2530
|
-
properties: {
|
|
2531
|
-
question: {
|
|
2532
|
-
type: 'string',
|
|
2533
|
-
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
2534
|
-
},
|
|
2535
|
-
choices: {
|
|
2536
|
-
type: 'array',
|
|
2537
|
-
description: 'All of the choices.',
|
|
2538
|
-
items: {
|
|
2539
|
-
type: 'object',
|
|
2540
|
-
properties: {
|
|
2541
|
-
choice: {
|
|
2542
|
-
type: 'string',
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
},
|
|
2548
|
-
required: ['question', 'choices'],
|
|
2549
|
-
};
|
|
2550
|
-
}
|
|
2551
|
-
async execute(context, params) {
|
|
2552
|
-
var _a;
|
|
2553
|
-
if (typeof params !== 'object' || params === null || !params.question || !params.choices) {
|
|
2554
|
-
throw new Error('Invalid parameters. Expected an object with a "question" and "choices" property.');
|
|
2555
|
-
}
|
|
2556
|
-
const question = params.question;
|
|
2557
|
-
const choices = params.choices.map((e) => e.choice);
|
|
2558
|
-
logger.debug("question: " + question);
|
|
2559
|
-
logger.debug("choices: " + choices);
|
|
2560
|
-
let onHumanInputSingleChoice = (_a = context.callback) === null || _a === void 0 ? void 0 : _a.hooks.onHumanInputSingleChoice;
|
|
2561
|
-
if (onHumanInputSingleChoice) {
|
|
2562
|
-
let answer;
|
|
2563
|
-
try {
|
|
2564
|
-
answer = await onHumanInputSingleChoice(question, choices);
|
|
2565
|
-
}
|
|
2566
|
-
catch (e) {
|
|
2567
|
-
logger.warn(e);
|
|
2568
|
-
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2569
|
-
}
|
|
2570
|
-
logger.debug("answer: " + answer);
|
|
2571
|
-
return { status: "OK", answer: answer };
|
|
2572
|
-
}
|
|
2573
|
-
else {
|
|
2574
|
-
logger.error("`onHumanInputSingleChoice` not implemented");
|
|
2575
|
-
return { status: "Error: Cannot get user's answer.", answer: "" };
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
class HumanInputMultipleChoice {
|
|
2580
|
-
constructor() {
|
|
2581
|
-
this.name = 'human_input_multiple_choice';
|
|
2582
|
-
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.';
|
|
2583
|
-
this.input_schema = {
|
|
2584
|
-
type: 'object',
|
|
2585
|
-
properties: {
|
|
2586
|
-
question: {
|
|
2587
|
-
type: 'string',
|
|
2588
|
-
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
2589
|
-
},
|
|
2590
|
-
choices: {
|
|
2591
|
-
type: 'array',
|
|
2592
|
-
description: 'All of the choices.',
|
|
2593
|
-
items: {
|
|
2594
|
-
type: 'object',
|
|
2595
|
-
properties: {
|
|
2596
|
-
choice: {
|
|
2597
|
-
type: 'string',
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
},
|
|
2603
|
-
required: ['question', 'choices'],
|
|
2604
|
-
};
|
|
2605
|
-
}
|
|
2606
|
-
async execute(context, params) {
|
|
2607
|
-
var _a;
|
|
2608
|
-
if (typeof params !== 'object' || params === null || !params.question || !params.choices) {
|
|
2609
|
-
throw new Error('Invalid parameters. Expected an object with a "question" and "choices" property.');
|
|
2610
|
-
}
|
|
2611
|
-
const question = params.question;
|
|
2612
|
-
const choices = params.choices.map((e) => e.choice);
|
|
2613
|
-
logger.debug("question: " + question);
|
|
2614
|
-
logger.debug("choices: " + choices);
|
|
2615
|
-
let onHumanInputMultipleChoice = (_a = context.callback) === null || _a === void 0 ? void 0 : _a.hooks.onHumanInputMultipleChoice;
|
|
2616
|
-
if (onHumanInputMultipleChoice) {
|
|
2617
|
-
let answer;
|
|
2618
|
-
try {
|
|
2619
|
-
answer = await onHumanInputMultipleChoice(question, choices);
|
|
2620
|
-
}
|
|
2621
|
-
catch (e) {
|
|
2622
|
-
logger.warn(e);
|
|
2623
|
-
return { status: "Error: Cannot get user's answer.", answer: [] };
|
|
2624
|
-
}
|
|
2625
|
-
logger.debug("answer: " + answer);
|
|
2626
|
-
return { status: "OK", answer: answer };
|
|
2627
|
-
}
|
|
2628
|
-
else {
|
|
2629
|
-
logger.error("`onHumanInputMultipleChoice` not implemented");
|
|
2630
|
-
return { status: "Error: Cannot get user's answer.", answer: [] };
|
|
2631
|
-
}
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
class HumanOperate {
|
|
2635
|
-
constructor() {
|
|
2636
|
-
this.name = 'human_operate';
|
|
2637
|
-
this.description = `Use this tool when one of following appears:
|
|
2638
|
-
1. Authentication (such as logging in, entering a verification code, etc.)
|
|
2639
|
-
2. External system operations (such as uploading files, selecting a file save location, scanning documents, taking photos, paying, authorization, etc.)
|
|
2640
|
-
|
|
2641
|
-
NOTE: You should ONLY use this tool in the scenarios above.
|
|
2642
|
-
|
|
2643
|
-
When calling this tool to transfer control to the user, please explain in detail:
|
|
2644
|
-
1. Why user intervention is required
|
|
2645
|
-
2. What operations the user needs to perform`;
|
|
2646
|
-
this.input_schema = {
|
|
2647
|
-
type: 'object',
|
|
2648
|
-
properties: {
|
|
2649
|
-
reason: {
|
|
2650
|
-
type: 'string',
|
|
2651
|
-
description: 'The reason why you need to transfer control. Should follow the format: "Please ..., and click the "Completed" button to continue.".',
|
|
2652
|
-
},
|
|
2653
|
-
},
|
|
2654
|
-
required: ['reason'],
|
|
2655
|
-
};
|
|
2656
|
-
}
|
|
2657
|
-
async execute(context, params) {
|
|
2658
|
-
var _a;
|
|
2659
|
-
if (typeof params !== 'object' || params === null || !params.reason) {
|
|
2660
|
-
throw new Error('Invalid parameters. Expected an object with a "reason" property.');
|
|
2661
|
-
}
|
|
2662
|
-
const reason = params.reason;
|
|
2663
|
-
logger.debug("reason: " + reason);
|
|
2664
|
-
let onHumanOperate = (_a = context.callback) === null || _a === void 0 ? void 0 : _a.hooks.onHumanOperate;
|
|
2665
|
-
if (onHumanOperate) {
|
|
2666
|
-
let userOperation;
|
|
2667
|
-
try {
|
|
2668
|
-
userOperation = await onHumanOperate(reason);
|
|
2669
|
-
}
|
|
2670
|
-
catch (e) {
|
|
2671
|
-
logger.warn(e);
|
|
2672
|
-
return { status: "Error: Cannot get user's operation.", userOperation: "" };
|
|
2673
|
-
}
|
|
2674
|
-
logger.debug("userOperation: " + userOperation);
|
|
2675
|
-
if (userOperation == "") {
|
|
2676
|
-
return { status: "OK", userOperation: "Done. Please take a screenshot to ensure the result." };
|
|
2677
|
-
}
|
|
2678
|
-
else {
|
|
2679
|
-
return { status: "OK", userOperation: userOperation + "\n\nPlease take a screenshot to ensure the result." };
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
else {
|
|
2683
|
-
logger.error("`onHumanOperate` not implemented");
|
|
2684
|
-
return { status: "Error: Cannot get user's operation.", userOperation: "" };
|
|
2685
|
-
}
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
class DocumentAgentTool {
|
|
2690
|
-
constructor() {
|
|
2691
|
-
this.name = 'document_agent';
|
|
2692
|
-
this.description = 'A document agent that can help you write document or long text, e.g. research report, email draft, summary.';
|
|
2693
|
-
this.input_schema = {
|
|
2694
|
-
"type": "object",
|
|
2695
|
-
"properties": {
|
|
2696
|
-
"type": {
|
|
2697
|
-
"type": "string",
|
|
2698
|
-
"description": "The type of document to be created (e.g., 'report', 'presentation', 'article')."
|
|
2699
|
-
},
|
|
2700
|
-
"title": {
|
|
2701
|
-
"type": "string",
|
|
2702
|
-
"description": "The title of the document."
|
|
2703
|
-
},
|
|
2704
|
-
"background": {
|
|
2705
|
-
"type": "string",
|
|
2706
|
-
"description": "The background information or target for the document."
|
|
2707
|
-
},
|
|
2708
|
-
"keypoints": {
|
|
2709
|
-
"type": "string",
|
|
2710
|
-
"description": "A summary of the key points or main ideas to be included in the document."
|
|
2711
|
-
},
|
|
2712
|
-
"style": {
|
|
2713
|
-
"type": "string",
|
|
2714
|
-
"description": "The desired style or tone of the document (e.g., 'formal', 'casual', 'academic')."
|
|
2715
|
-
},
|
|
2716
|
-
},
|
|
2717
|
-
"required": ["type", "title", "background", "keypoints"],
|
|
2718
|
-
};
|
|
2719
|
-
}
|
|
2720
|
-
async execute(context, params) {
|
|
2721
|
-
params.references = context.variables;
|
|
2722
|
-
const messages = [
|
|
2723
|
-
{
|
|
2724
|
-
role: 'system',
|
|
2725
|
-
content: 'You are an excellent writer, skilled at composing various types of copywriting and texts in different styles. You can draft documents based on the title, background, or reference materials provided by clients. Now, the client will provide you with a lot of information, including the type of copywriting, title, background, key points, style, and reference materials. Please write a document in Markdown format.',
|
|
2726
|
-
},
|
|
2727
|
-
{
|
|
2728
|
-
role: 'user',
|
|
2729
|
-
content: JSON.stringify(params),
|
|
2730
|
-
},
|
|
2731
|
-
];
|
|
2732
|
-
const llmParams = { maxTokens: 8192 };
|
|
2733
|
-
const response = await context.llmProvider.generateText(messages, llmParams);
|
|
2734
|
-
const content = typeof response.content == 'string' ? response.content : response.content[0].text;
|
|
2735
|
-
context.variables.set("workflow_transcript", content);
|
|
2736
|
-
return { status: "OK", content };
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
var tools = /*#__PURE__*/Object.freeze({
|
|
2741
|
-
__proto__: null,
|
|
2742
|
-
BrowserAction: BrowserAction,
|
|
2743
|
-
CancelWorkflow: CancelWorkflow,
|
|
2744
|
-
DocumentAgentTool: DocumentAgentTool,
|
|
2745
|
-
ExportFile: ExportFile,
|
|
2746
|
-
ExtractContent: ExtractContent,
|
|
2747
|
-
GetAllTabs: GetAllTabs,
|
|
2748
|
-
HumanInputMultipleChoice: HumanInputMultipleChoice,
|
|
2749
|
-
HumanInputSingleChoice: HumanInputSingleChoice,
|
|
2750
|
-
HumanInputText: HumanInputText,
|
|
2751
|
-
HumanOperate: HumanOperate,
|
|
2752
|
-
OpenUrl: OpenUrl,
|
|
2753
|
-
RequestLogin: RequestLogin,
|
|
2754
|
-
Screenshot: Screenshot,
|
|
2755
|
-
SwitchTab: SwitchTab,
|
|
2756
|
-
TabManagement: TabManagement,
|
|
2757
|
-
WebSearch: WebSearch
|
|
2758
|
-
});
|
|
2759
|
-
|
|
2760
|
-
async function pub(chromeProxy, tabId, event, params) {
|
|
2761
|
-
return await chromeProxy.tabs.sendMessage(tabId, {
|
|
2762
|
-
type: 'eko:message',
|
|
2763
|
-
event,
|
|
2764
|
-
params,
|
|
2765
|
-
});
|
|
2766
|
-
}
|
|
2767
|
-
async function getLLMConfig(chromeProxy, name = 'llmConfig') {
|
|
2768
|
-
let result = await chromeProxy.storage.sync.get([name]);
|
|
2769
|
-
return result[name];
|
|
2770
|
-
}
|
|
2771
|
-
function loadTools() {
|
|
2772
|
-
let toolsMap = new Map();
|
|
2773
|
-
for (const key in tools) {
|
|
2774
|
-
let tool = tools[key];
|
|
2775
|
-
if (typeof tool === 'function' && tool.prototype && 'execute' in tool.prototype) {
|
|
2776
|
-
try {
|
|
2777
|
-
let instance = new tool();
|
|
2778
|
-
toolsMap.set(instance.name || key, instance);
|
|
2779
|
-
}
|
|
2780
|
-
catch (e) {
|
|
2781
|
-
logger.error(`Failed to instantiate ${key}:`, e);
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
return toolsMap;
|
|
2786
|
-
}
|
|
2787
|
-
|
|
2788
|
-
exports.browser = browser;
|
|
2789
|
-
exports.getLLMConfig = getLLMConfig;
|
|
2790
|
-
exports.loadTools = loadTools;
|
|
2791
|
-
exports.pub = pub;
|
|
2792
|
-
exports.tools = tools;
|
|
2793
|
-
exports.utils = utils;
|