@eko-ai/eko 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extension/tools/browser_use.d.ts +4 -3
- package/dist/extension/tools/open_url.d.ts +4 -3
- package/dist/extension/tools/tool_returns_screenshot.d.ts +8 -0
- package/dist/extension/utils.d.ts +1 -1
- package/dist/extension.cjs.js +44 -73
- package/dist/extension.esm.js +44 -73
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/nodejs.cjs.js +8 -6
- package/dist/nodejs.esm.js +8 -6
- package/dist/web.cjs.js +8 -6
- package/dist/web.esm.js +8 -6
- package/package.json +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { BrowserUseParam, BrowserUseResult } from '../../types/tools.types';
|
|
2
|
-
import {
|
|
2
|
+
import { InputSchema, ExecutionContext } from '../../types/action.types';
|
|
3
|
+
import { ToolReturnsScreenshot } from './tool_returns_screenshot';
|
|
3
4
|
/**
|
|
4
5
|
* Browser Use for general
|
|
5
6
|
*/
|
|
6
|
-
export declare class BrowserUse
|
|
7
|
+
export declare class BrowserUse extends ToolReturnsScreenshot<BrowserUseParam> {
|
|
7
8
|
name: string;
|
|
8
9
|
description: string;
|
|
9
10
|
input_schema: InputSchema;
|
|
@@ -14,6 +15,6 @@ export declare class BrowserUse implements Tool<BrowserUseParam, BrowserUseResul
|
|
|
14
15
|
* @param {*} params { action: 'input_text', index: 1, text: 'string' }
|
|
15
16
|
* @returns > { success: true, image?: { type: 'base64', media_type: 'image/jpeg', data: '/9j...' }, text?: string }
|
|
16
17
|
*/
|
|
17
|
-
|
|
18
|
+
realExecute(context: ExecutionContext, params: BrowserUseParam): Promise<BrowserUseResult>;
|
|
18
19
|
destroy(context: ExecutionContext): void;
|
|
19
20
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { OpenUrlParam, OpenUrlResult } from '../../types/tools.types';
|
|
2
|
-
import {
|
|
2
|
+
import { InputSchema, ExecutionContext } from '../../types/action.types';
|
|
3
|
+
import { ToolReturnsScreenshot } from './tool_returns_screenshot';
|
|
3
4
|
/**
|
|
4
5
|
* Open Url
|
|
5
6
|
*/
|
|
6
|
-
export declare class OpenUrl
|
|
7
|
+
export declare class OpenUrl extends ToolReturnsScreenshot<OpenUrlParam> {
|
|
7
8
|
name: string;
|
|
8
9
|
description: string;
|
|
9
10
|
input_schema: InputSchema;
|
|
@@ -14,5 +15,5 @@ export declare class OpenUrl implements Tool<OpenUrlParam, OpenUrlResult> {
|
|
|
14
15
|
* @param {*} params { url: 'https://www.google.com', newWindow: true }
|
|
15
16
|
* @returns > { tabId, windowId, title, success: true }
|
|
16
17
|
*/
|
|
17
|
-
|
|
18
|
+
realExecute(context: ExecutionContext, params: OpenUrlParam): Promise<OpenUrlResult>;
|
|
18
19
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BrowserUseResult, ExecutionContext, InputSchema, Tool } from "@/types";
|
|
2
|
+
export declare abstract class ToolReturnsScreenshot<T> implements Tool<T, BrowserUseResult> {
|
|
3
|
+
abstract name: string;
|
|
4
|
+
abstract description: string;
|
|
5
|
+
abstract input_schema: InputSchema;
|
|
6
|
+
abstract realExecute(context: ExecutionContext, params: T): Promise<any>;
|
|
7
|
+
execute(context: ExecutionContext, params: T): Promise<BrowserUseResult>;
|
|
8
|
+
}
|
|
@@ -2,7 +2,7 @@ import { ExecutionContext } from '../types/action.types';
|
|
|
2
2
|
export declare function getWindowId(context: ExecutionContext): Promise<number>;
|
|
3
3
|
export declare function getTabId(context: ExecutionContext): Promise<number>;
|
|
4
4
|
export declare function getCurrentTabId(chromeProxy: any, windowId?: number | undefined): Promise<number | undefined>;
|
|
5
|
-
export declare function open_new_tab(chromeProxy: any, url: string,
|
|
5
|
+
export declare function open_new_tab(chromeProxy: any, url: string, windowId?: number): Promise<chrome.tabs.Tab>;
|
|
6
6
|
export declare function executeScript(chromeProxy: any, tabId: number, func: any, args: any[]): Promise<any>;
|
|
7
7
|
export declare function waitForTabComplete(chromeProxy: any, tabId: number, timeout?: number): Promise<chrome.tabs.Tab>;
|
|
8
8
|
export declare function doesTabExists(chromeProxy: any, tabId: number): Promise<unknown>;
|
package/dist/extension.cjs.js
CHANGED
|
@@ -101,41 +101,23 @@ function getCurrentTabId(chromeProxy, windowId) {
|
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
-
async function open_new_tab(chromeProxy, url,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
let window = await chromeProxy.windows.create({
|
|
108
|
-
type: 'normal',
|
|
109
|
-
state: 'maximized',
|
|
110
|
-
url: url,
|
|
111
|
-
});
|
|
104
|
+
async function open_new_tab(chromeProxy, url, windowId) {
|
|
105
|
+
if (!windowId) {
|
|
106
|
+
const window = await chromeProxy.windows.getCurrent();
|
|
112
107
|
windowId = window.id;
|
|
113
|
-
let tabs = window.tabs || [
|
|
114
|
-
await chromeProxy.tabs.create({
|
|
115
|
-
url: url,
|
|
116
|
-
windowId: windowId,
|
|
117
|
-
}),
|
|
118
|
-
];
|
|
119
|
-
tabId = tabs[0].id;
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
if (!windowId) {
|
|
123
|
-
const window = await chromeProxy.windows.getCurrent();
|
|
124
|
-
windowId = window.id;
|
|
125
|
-
}
|
|
126
|
-
console.log("windowId: " + windowId);
|
|
127
|
-
let tab = await chromeProxy.tabs.create({
|
|
128
|
-
url: url,
|
|
129
|
-
windowId: windowId,
|
|
130
|
-
});
|
|
131
|
-
console.log("chromeProxy.tabs.create() done");
|
|
132
|
-
tabId = tab.id;
|
|
133
108
|
}
|
|
134
|
-
|
|
109
|
+
console.log("windowId: " + windowId);
|
|
110
|
+
let tab = await chromeProxy.tabs.create({
|
|
111
|
+
url: url,
|
|
112
|
+
windowId: windowId,
|
|
113
|
+
});
|
|
114
|
+
console.log("chromeProxy.tabs.create() done");
|
|
115
|
+
let tabId = tab.id;
|
|
116
|
+
let completedTab = await waitForTabComplete(chromeProxy, tabId);
|
|
135
117
|
console.log("waitForTabComplete() done");
|
|
136
118
|
await sleep(200);
|
|
137
119
|
console.log("sleep() done");
|
|
138
|
-
return
|
|
120
|
+
return completedTab;
|
|
139
121
|
}
|
|
140
122
|
async function executeScript(chromeProxy, tabId, func, args) {
|
|
141
123
|
let frameResults = await chromeProxy.scripting.executeScript({
|
|
@@ -772,11 +754,22 @@ var browser = /*#__PURE__*/Object.freeze({
|
|
|
772
754
|
type_by: type_by
|
|
773
755
|
});
|
|
774
756
|
|
|
757
|
+
class ToolReturnsScreenshot {
|
|
758
|
+
async execute(context, params) {
|
|
759
|
+
const realResult = await this.realExecute(context, params);
|
|
760
|
+
console.log("debug realResult...", realResult);
|
|
761
|
+
let instance = new BrowserUse();
|
|
762
|
+
const image = await instance.realExecute(context, { action: "screenshot_extract_element" });
|
|
763
|
+
return image;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
775
767
|
/**
|
|
776
768
|
* Browser Use for general
|
|
777
769
|
*/
|
|
778
|
-
class BrowserUse {
|
|
770
|
+
class BrowserUse extends ToolReturnsScreenshot {
|
|
779
771
|
constructor() {
|
|
772
|
+
super();
|
|
780
773
|
this.name = 'browser_use';
|
|
781
774
|
this.description = `Use structured commands to interact with the browser, manipulating page elements through screenshots and webpage element extraction.
|
|
782
775
|
* 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.
|
|
@@ -839,7 +832,7 @@ class BrowserUse {
|
|
|
839
832
|
* @param {*} params { action: 'input_text', index: 1, text: 'string' }
|
|
840
833
|
* @returns > { success: true, image?: { type: 'base64', media_type: 'image/jpeg', data: '/9j...' }, text?: string }
|
|
841
834
|
*/
|
|
842
|
-
async
|
|
835
|
+
async realExecute(context, params) {
|
|
843
836
|
var _a;
|
|
844
837
|
console.log("execute 'browser_use'...");
|
|
845
838
|
try {
|
|
@@ -967,12 +960,6 @@ class BrowserUse {
|
|
|
967
960
|
}
|
|
968
961
|
console.log("execute 'browser_use'...done, result=");
|
|
969
962
|
console.log(result);
|
|
970
|
-
if (params.action != "screenshot_extract_element") {
|
|
971
|
-
console.log("as this action is has not screenshoted, take it now...");
|
|
972
|
-
let instance = new BrowserUse();
|
|
973
|
-
result = await instance.execute(context, { action: "screenshot_extract_element" });
|
|
974
|
-
console.log("as this action is has not screenshoted, take it now...done");
|
|
975
|
-
}
|
|
976
963
|
return result;
|
|
977
964
|
}
|
|
978
965
|
catch (e) {
|
|
@@ -1078,10 +1065,10 @@ class ExportFile {
|
|
|
1078
1065
|
let tab;
|
|
1079
1066
|
const url = 'https://www.google.com';
|
|
1080
1067
|
if (context.ekoConfig.workingWindowId) {
|
|
1081
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url,
|
|
1068
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, context.ekoConfig.workingWindowId);
|
|
1082
1069
|
}
|
|
1083
1070
|
else {
|
|
1084
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url
|
|
1071
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url);
|
|
1085
1072
|
}
|
|
1086
1073
|
(_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);
|
|
1087
1074
|
let tabId = tab.id;
|
|
@@ -1179,8 +1166,9 @@ class GetAllTabs {
|
|
|
1179
1166
|
/**
|
|
1180
1167
|
* Open Url
|
|
1181
1168
|
*/
|
|
1182
|
-
class OpenUrl {
|
|
1169
|
+
class OpenUrl extends ToolReturnsScreenshot {
|
|
1183
1170
|
constructor() {
|
|
1171
|
+
super();
|
|
1184
1172
|
this.name = 'open_url';
|
|
1185
1173
|
this.description = 'Open the specified URL link in browser window';
|
|
1186
1174
|
this.input_schema = {
|
|
@@ -1204,7 +1192,7 @@ class OpenUrl {
|
|
|
1204
1192
|
* @param {*} params { url: 'https://www.google.com', newWindow: true }
|
|
1205
1193
|
* @returns > { tabId, windowId, title, success: true }
|
|
1206
1194
|
*/
|
|
1207
|
-
async
|
|
1195
|
+
async realExecute(context, params) {
|
|
1208
1196
|
var _a, _b, _c, _d, _e, _f;
|
|
1209
1197
|
console.log('Starting execute function with context:', context, 'and params:', params);
|
|
1210
1198
|
// 参数验证
|
|
@@ -1232,7 +1220,7 @@ class OpenUrl {
|
|
|
1232
1220
|
let tab;
|
|
1233
1221
|
if (newWindow) {
|
|
1234
1222
|
console.log('Opening new tab in a new window.');
|
|
1235
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url
|
|
1223
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url);
|
|
1236
1224
|
(_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);
|
|
1237
1225
|
console.log('New tab created in a new window:', tab);
|
|
1238
1226
|
}
|
|
@@ -1240,7 +1228,7 @@ class OpenUrl {
|
|
|
1240
1228
|
let windowId = context.ekoConfig.workingWindowId ? context.ekoConfig.workingWindowId : await getWindowId(context);
|
|
1241
1229
|
console.log('Using existing window with ID:', windowId);
|
|
1242
1230
|
try {
|
|
1243
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url,
|
|
1231
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, windowId);
|
|
1244
1232
|
console.log("Calling hook...");
|
|
1245
1233
|
(_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);
|
|
1246
1234
|
console.log('New tab created in existing window:', tab);
|
|
@@ -1338,7 +1326,7 @@ class TabManagement {
|
|
|
1338
1326
|
* @returns > { result, success: true }
|
|
1339
1327
|
*/
|
|
1340
1328
|
async execute(context, params) {
|
|
1341
|
-
var _a, _b, _c
|
|
1329
|
+
var _a, _b, _c;
|
|
1342
1330
|
if (params === null || !params.command) {
|
|
1343
1331
|
throw new Error('Invalid parameters. Expected an object with a "command" property.');
|
|
1344
1332
|
}
|
|
@@ -1409,31 +1397,12 @@ class TabManagement {
|
|
|
1409
1397
|
}
|
|
1410
1398
|
else if (command.startsWith('new_tab')) {
|
|
1411
1399
|
let url = command.replace('new_tab', '').replace('[', '').replace(']', '').replace(/"/g, '').trim();
|
|
1412
|
-
|
|
1413
|
-
let
|
|
1414
|
-
|
|
1415
|
-
if (newWindow) {
|
|
1416
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, true);
|
|
1417
|
-
(_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);
|
|
1418
|
-
}
|
|
1419
|
-
else {
|
|
1420
|
-
let windowId = await getWindowId(context);
|
|
1421
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, false, windowId);
|
|
1422
|
-
(_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);
|
|
1423
|
-
}
|
|
1424
|
-
let windowId = tab.windowId;
|
|
1400
|
+
let windowId = await getWindowId(context);
|
|
1401
|
+
let tab = await open_new_tab(context.ekoConfig.chromeProxy, url, windowId);
|
|
1402
|
+
(_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);
|
|
1425
1403
|
let tabId = tab.id;
|
|
1426
1404
|
context.variables.set('windowId', windowId);
|
|
1427
1405
|
context.variables.set('tabId', tabId);
|
|
1428
|
-
if (newWindow) {
|
|
1429
|
-
let windowIds = context.variables.get('windowIds');
|
|
1430
|
-
if (windowIds) {
|
|
1431
|
-
windowIds.push(windowId);
|
|
1432
|
-
}
|
|
1433
|
-
else {
|
|
1434
|
-
context.variables.set('windowIds', [windowId]);
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
1406
|
let tabInfo = {
|
|
1438
1407
|
tabId: tab.id,
|
|
1439
1408
|
windowId: tab.windowId,
|
|
@@ -1887,7 +1856,7 @@ class HumanInputText {
|
|
|
1887
1856
|
properties: {
|
|
1888
1857
|
question: {
|
|
1889
1858
|
type: 'string',
|
|
1890
|
-
description: 'Ask the user here.',
|
|
1859
|
+
description: 'Ask the user here. Should follow the format: "Please input ...".',
|
|
1891
1860
|
},
|
|
1892
1861
|
},
|
|
1893
1862
|
required: ['question'],
|
|
@@ -1928,7 +1897,7 @@ class HumanInputSingleChoice {
|
|
|
1928
1897
|
properties: {
|
|
1929
1898
|
question: {
|
|
1930
1899
|
type: 'string',
|
|
1931
|
-
description: 'Ask the user here.',
|
|
1900
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
1932
1901
|
},
|
|
1933
1902
|
choices: {
|
|
1934
1903
|
type: 'array',
|
|
@@ -1983,7 +1952,7 @@ class HumanInputMultipleChoice {
|
|
|
1983
1952
|
properties: {
|
|
1984
1953
|
question: {
|
|
1985
1954
|
type: 'string',
|
|
1986
|
-
description: 'Ask the user here.',
|
|
1955
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
1987
1956
|
},
|
|
1988
1957
|
choices: {
|
|
1989
1958
|
type: 'array',
|
|
@@ -2032,10 +2001,12 @@ class HumanInputMultipleChoice {
|
|
|
2032
2001
|
class HumanOperate {
|
|
2033
2002
|
constructor() {
|
|
2034
2003
|
this.name = 'human_operate';
|
|
2035
|
-
this.description = `Use this tool when
|
|
2036
|
-
Usage scenarios include:
|
|
2004
|
+
this.description = `Use this tool when one of following appears:
|
|
2037
2005
|
1. Authentication (such as logging in, entering a verification code, etc.)
|
|
2038
2006
|
2. External system operations (such as uploading files, selecting a file save location, scanning documents, taking photos, paying, authorization, etc.)
|
|
2007
|
+
|
|
2008
|
+
NOTE: You should ONLY use this tool in the scenarios above.
|
|
2009
|
+
|
|
2039
2010
|
When calling this tool to transfer control to the user, please explain in detail:
|
|
2040
2011
|
1. Why user intervention is required
|
|
2041
2012
|
2. What operations the user needs to perform`;
|
|
@@ -2044,7 +2015,7 @@ When calling this tool to transfer control to the user, please explain in detail
|
|
|
2044
2015
|
properties: {
|
|
2045
2016
|
reason: {
|
|
2046
2017
|
type: 'string',
|
|
2047
|
-
description: 'The reason why you need to transfer control.',
|
|
2018
|
+
description: 'The reason why you need to transfer control. Should follow the format: "Please ..., and click the "Completed" button to continue.".',
|
|
2048
2019
|
},
|
|
2049
2020
|
},
|
|
2050
2021
|
required: ['reason'],
|
package/dist/extension.esm.js
CHANGED
|
@@ -99,41 +99,23 @@ function getCurrentTabId(chromeProxy, windowId) {
|
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
|
-
async function open_new_tab(chromeProxy, url,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
let window = await chromeProxy.windows.create({
|
|
106
|
-
type: 'normal',
|
|
107
|
-
state: 'maximized',
|
|
108
|
-
url: url,
|
|
109
|
-
});
|
|
102
|
+
async function open_new_tab(chromeProxy, url, windowId) {
|
|
103
|
+
if (!windowId) {
|
|
104
|
+
const window = await chromeProxy.windows.getCurrent();
|
|
110
105
|
windowId = window.id;
|
|
111
|
-
let tabs = window.tabs || [
|
|
112
|
-
await chromeProxy.tabs.create({
|
|
113
|
-
url: url,
|
|
114
|
-
windowId: windowId,
|
|
115
|
-
}),
|
|
116
|
-
];
|
|
117
|
-
tabId = tabs[0].id;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
if (!windowId) {
|
|
121
|
-
const window = await chromeProxy.windows.getCurrent();
|
|
122
|
-
windowId = window.id;
|
|
123
|
-
}
|
|
124
|
-
console.log("windowId: " + windowId);
|
|
125
|
-
let tab = await chromeProxy.tabs.create({
|
|
126
|
-
url: url,
|
|
127
|
-
windowId: windowId,
|
|
128
|
-
});
|
|
129
|
-
console.log("chromeProxy.tabs.create() done");
|
|
130
|
-
tabId = tab.id;
|
|
131
106
|
}
|
|
132
|
-
|
|
107
|
+
console.log("windowId: " + windowId);
|
|
108
|
+
let tab = await chromeProxy.tabs.create({
|
|
109
|
+
url: url,
|
|
110
|
+
windowId: windowId,
|
|
111
|
+
});
|
|
112
|
+
console.log("chromeProxy.tabs.create() done");
|
|
113
|
+
let tabId = tab.id;
|
|
114
|
+
let completedTab = await waitForTabComplete(chromeProxy, tabId);
|
|
133
115
|
console.log("waitForTabComplete() done");
|
|
134
116
|
await sleep(200);
|
|
135
117
|
console.log("sleep() done");
|
|
136
|
-
return
|
|
118
|
+
return completedTab;
|
|
137
119
|
}
|
|
138
120
|
async function executeScript(chromeProxy, tabId, func, args) {
|
|
139
121
|
let frameResults = await chromeProxy.scripting.executeScript({
|
|
@@ -770,11 +752,22 @@ var browser = /*#__PURE__*/Object.freeze({
|
|
|
770
752
|
type_by: type_by
|
|
771
753
|
});
|
|
772
754
|
|
|
755
|
+
class ToolReturnsScreenshot {
|
|
756
|
+
async execute(context, params) {
|
|
757
|
+
const realResult = await this.realExecute(context, params);
|
|
758
|
+
console.log("debug realResult...", realResult);
|
|
759
|
+
let instance = new BrowserUse();
|
|
760
|
+
const image = await instance.realExecute(context, { action: "screenshot_extract_element" });
|
|
761
|
+
return image;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
773
765
|
/**
|
|
774
766
|
* Browser Use for general
|
|
775
767
|
*/
|
|
776
|
-
class BrowserUse {
|
|
768
|
+
class BrowserUse extends ToolReturnsScreenshot {
|
|
777
769
|
constructor() {
|
|
770
|
+
super();
|
|
778
771
|
this.name = 'browser_use';
|
|
779
772
|
this.description = `Use structured commands to interact with the browser, manipulating page elements through screenshots and webpage element extraction.
|
|
780
773
|
* 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.
|
|
@@ -837,7 +830,7 @@ class BrowserUse {
|
|
|
837
830
|
* @param {*} params { action: 'input_text', index: 1, text: 'string' }
|
|
838
831
|
* @returns > { success: true, image?: { type: 'base64', media_type: 'image/jpeg', data: '/9j...' }, text?: string }
|
|
839
832
|
*/
|
|
840
|
-
async
|
|
833
|
+
async realExecute(context, params) {
|
|
841
834
|
var _a;
|
|
842
835
|
console.log("execute 'browser_use'...");
|
|
843
836
|
try {
|
|
@@ -965,12 +958,6 @@ class BrowserUse {
|
|
|
965
958
|
}
|
|
966
959
|
console.log("execute 'browser_use'...done, result=");
|
|
967
960
|
console.log(result);
|
|
968
|
-
if (params.action != "screenshot_extract_element") {
|
|
969
|
-
console.log("as this action is has not screenshoted, take it now...");
|
|
970
|
-
let instance = new BrowserUse();
|
|
971
|
-
result = await instance.execute(context, { action: "screenshot_extract_element" });
|
|
972
|
-
console.log("as this action is has not screenshoted, take it now...done");
|
|
973
|
-
}
|
|
974
961
|
return result;
|
|
975
962
|
}
|
|
976
963
|
catch (e) {
|
|
@@ -1076,10 +1063,10 @@ class ExportFile {
|
|
|
1076
1063
|
let tab;
|
|
1077
1064
|
const url = 'https://www.google.com';
|
|
1078
1065
|
if (context.ekoConfig.workingWindowId) {
|
|
1079
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url,
|
|
1066
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, context.ekoConfig.workingWindowId);
|
|
1080
1067
|
}
|
|
1081
1068
|
else {
|
|
1082
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url
|
|
1069
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url);
|
|
1083
1070
|
}
|
|
1084
1071
|
(_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);
|
|
1085
1072
|
let tabId = tab.id;
|
|
@@ -1177,8 +1164,9 @@ class GetAllTabs {
|
|
|
1177
1164
|
/**
|
|
1178
1165
|
* Open Url
|
|
1179
1166
|
*/
|
|
1180
|
-
class OpenUrl {
|
|
1167
|
+
class OpenUrl extends ToolReturnsScreenshot {
|
|
1181
1168
|
constructor() {
|
|
1169
|
+
super();
|
|
1182
1170
|
this.name = 'open_url';
|
|
1183
1171
|
this.description = 'Open the specified URL link in browser window';
|
|
1184
1172
|
this.input_schema = {
|
|
@@ -1202,7 +1190,7 @@ class OpenUrl {
|
|
|
1202
1190
|
* @param {*} params { url: 'https://www.google.com', newWindow: true }
|
|
1203
1191
|
* @returns > { tabId, windowId, title, success: true }
|
|
1204
1192
|
*/
|
|
1205
|
-
async
|
|
1193
|
+
async realExecute(context, params) {
|
|
1206
1194
|
var _a, _b, _c, _d, _e, _f;
|
|
1207
1195
|
console.log('Starting execute function with context:', context, 'and params:', params);
|
|
1208
1196
|
// 参数验证
|
|
@@ -1230,7 +1218,7 @@ class OpenUrl {
|
|
|
1230
1218
|
let tab;
|
|
1231
1219
|
if (newWindow) {
|
|
1232
1220
|
console.log('Opening new tab in a new window.');
|
|
1233
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url
|
|
1221
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url);
|
|
1234
1222
|
(_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);
|
|
1235
1223
|
console.log('New tab created in a new window:', tab);
|
|
1236
1224
|
}
|
|
@@ -1238,7 +1226,7 @@ class OpenUrl {
|
|
|
1238
1226
|
let windowId = context.ekoConfig.workingWindowId ? context.ekoConfig.workingWindowId : await getWindowId(context);
|
|
1239
1227
|
console.log('Using existing window with ID:', windowId);
|
|
1240
1228
|
try {
|
|
1241
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url,
|
|
1229
|
+
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, windowId);
|
|
1242
1230
|
console.log("Calling hook...");
|
|
1243
1231
|
(_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);
|
|
1244
1232
|
console.log('New tab created in existing window:', tab);
|
|
@@ -1336,7 +1324,7 @@ class TabManagement {
|
|
|
1336
1324
|
* @returns > { result, success: true }
|
|
1337
1325
|
*/
|
|
1338
1326
|
async execute(context, params) {
|
|
1339
|
-
var _a, _b, _c
|
|
1327
|
+
var _a, _b, _c;
|
|
1340
1328
|
if (params === null || !params.command) {
|
|
1341
1329
|
throw new Error('Invalid parameters. Expected an object with a "command" property.');
|
|
1342
1330
|
}
|
|
@@ -1407,31 +1395,12 @@ class TabManagement {
|
|
|
1407
1395
|
}
|
|
1408
1396
|
else if (command.startsWith('new_tab')) {
|
|
1409
1397
|
let url = command.replace('new_tab', '').replace('[', '').replace(']', '').replace(/"/g, '').trim();
|
|
1410
|
-
|
|
1411
|
-
let
|
|
1412
|
-
|
|
1413
|
-
if (newWindow) {
|
|
1414
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, true);
|
|
1415
|
-
(_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);
|
|
1416
|
-
}
|
|
1417
|
-
else {
|
|
1418
|
-
let windowId = await getWindowId(context);
|
|
1419
|
-
tab = await open_new_tab(context.ekoConfig.chromeProxy, url, false, windowId);
|
|
1420
|
-
(_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);
|
|
1421
|
-
}
|
|
1422
|
-
let windowId = tab.windowId;
|
|
1398
|
+
let windowId = await getWindowId(context);
|
|
1399
|
+
let tab = await open_new_tab(context.ekoConfig.chromeProxy, url, windowId);
|
|
1400
|
+
(_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);
|
|
1423
1401
|
let tabId = tab.id;
|
|
1424
1402
|
context.variables.set('windowId', windowId);
|
|
1425
1403
|
context.variables.set('tabId', tabId);
|
|
1426
|
-
if (newWindow) {
|
|
1427
|
-
let windowIds = context.variables.get('windowIds');
|
|
1428
|
-
if (windowIds) {
|
|
1429
|
-
windowIds.push(windowId);
|
|
1430
|
-
}
|
|
1431
|
-
else {
|
|
1432
|
-
context.variables.set('windowIds', [windowId]);
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
1404
|
let tabInfo = {
|
|
1436
1405
|
tabId: tab.id,
|
|
1437
1406
|
windowId: tab.windowId,
|
|
@@ -1885,7 +1854,7 @@ class HumanInputText {
|
|
|
1885
1854
|
properties: {
|
|
1886
1855
|
question: {
|
|
1887
1856
|
type: 'string',
|
|
1888
|
-
description: 'Ask the user here.',
|
|
1857
|
+
description: 'Ask the user here. Should follow the format: "Please input ...".',
|
|
1889
1858
|
},
|
|
1890
1859
|
},
|
|
1891
1860
|
required: ['question'],
|
|
@@ -1926,7 +1895,7 @@ class HumanInputSingleChoice {
|
|
|
1926
1895
|
properties: {
|
|
1927
1896
|
question: {
|
|
1928
1897
|
type: 'string',
|
|
1929
|
-
description: 'Ask the user here.',
|
|
1898
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
1930
1899
|
},
|
|
1931
1900
|
choices: {
|
|
1932
1901
|
type: 'array',
|
|
@@ -1981,7 +1950,7 @@ class HumanInputMultipleChoice {
|
|
|
1981
1950
|
properties: {
|
|
1982
1951
|
question: {
|
|
1983
1952
|
type: 'string',
|
|
1984
|
-
description: 'Ask the user here.',
|
|
1953
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
1985
1954
|
},
|
|
1986
1955
|
choices: {
|
|
1987
1956
|
type: 'array',
|
|
@@ -2030,10 +1999,12 @@ class HumanInputMultipleChoice {
|
|
|
2030
1999
|
class HumanOperate {
|
|
2031
2000
|
constructor() {
|
|
2032
2001
|
this.name = 'human_operate';
|
|
2033
|
-
this.description = `Use this tool when
|
|
2034
|
-
Usage scenarios include:
|
|
2002
|
+
this.description = `Use this tool when one of following appears:
|
|
2035
2003
|
1. Authentication (such as logging in, entering a verification code, etc.)
|
|
2036
2004
|
2. External system operations (such as uploading files, selecting a file save location, scanning documents, taking photos, paying, authorization, etc.)
|
|
2005
|
+
|
|
2006
|
+
NOTE: You should ONLY use this tool in the scenarios above.
|
|
2007
|
+
|
|
2037
2008
|
When calling this tool to transfer control to the user, please explain in detail:
|
|
2038
2009
|
1. Why user intervention is required
|
|
2039
2010
|
2. What operations the user needs to perform`;
|
|
@@ -2042,7 +2013,7 @@ When calling this tool to transfer control to the user, please explain in detail
|
|
|
2042
2013
|
properties: {
|
|
2043
2014
|
reason: {
|
|
2044
2015
|
type: 'string',
|
|
2045
|
-
description: 'The reason why you need to transfer control.',
|
|
2016
|
+
description: 'The reason why you need to transfer control. Should follow the format: "Please ..., and click the "Completed" button to continue.".',
|
|
2046
2017
|
},
|
|
2047
2018
|
},
|
|
2048
2019
|
required: ['reason'],
|
package/dist/index.cjs.js
CHANGED
|
@@ -10519,7 +10519,7 @@ class Eko {
|
|
|
10519
10519
|
this.prompt = "";
|
|
10520
10520
|
this.tabs = [];
|
|
10521
10521
|
this.workflow = undefined;
|
|
10522
|
-
console.info("using Eko@" + "
|
|
10522
|
+
console.info("using Eko@" + "14df29b1e490dfffd3cffc0548ab7e6d1e2e219a");
|
|
10523
10523
|
console.warn("this version is POC, should not used for production");
|
|
10524
10524
|
this.llmProvider = LLMProviderFactory.buildLLMProvider(llmConfig);
|
|
10525
10525
|
this.ekoConfig = this.buildEkoConfig(ekoConfig);
|
package/dist/index.esm.js
CHANGED
|
@@ -10515,7 +10515,7 @@ class Eko {
|
|
|
10515
10515
|
this.prompt = "";
|
|
10516
10516
|
this.tabs = [];
|
|
10517
10517
|
this.workflow = undefined;
|
|
10518
|
-
console.info("using Eko@" + "
|
|
10518
|
+
console.info("using Eko@" + "14df29b1e490dfffd3cffc0548ab7e6d1e2e219a");
|
|
10519
10519
|
console.warn("this version is POC, should not used for production");
|
|
10520
10520
|
this.llmProvider = LLMProviderFactory.buildLLMProvider(llmConfig);
|
|
10521
10521
|
this.ekoConfig = this.buildEkoConfig(ekoConfig);
|
package/dist/nodejs.cjs.js
CHANGED
|
@@ -61,7 +61,7 @@ class HumanInputText {
|
|
|
61
61
|
properties: {
|
|
62
62
|
question: {
|
|
63
63
|
type: 'string',
|
|
64
|
-
description: 'Ask the user here.',
|
|
64
|
+
description: 'Ask the user here. Should follow the format: "Please input ...".',
|
|
65
65
|
},
|
|
66
66
|
},
|
|
67
67
|
required: ['question'],
|
|
@@ -102,7 +102,7 @@ class HumanInputSingleChoice {
|
|
|
102
102
|
properties: {
|
|
103
103
|
question: {
|
|
104
104
|
type: 'string',
|
|
105
|
-
description: 'Ask the user here.',
|
|
105
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
106
106
|
},
|
|
107
107
|
choices: {
|
|
108
108
|
type: 'array',
|
|
@@ -157,7 +157,7 @@ class HumanInputMultipleChoice {
|
|
|
157
157
|
properties: {
|
|
158
158
|
question: {
|
|
159
159
|
type: 'string',
|
|
160
|
-
description: 'Ask the user here.',
|
|
160
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
161
161
|
},
|
|
162
162
|
choices: {
|
|
163
163
|
type: 'array',
|
|
@@ -206,10 +206,12 @@ class HumanInputMultipleChoice {
|
|
|
206
206
|
class HumanOperate {
|
|
207
207
|
constructor() {
|
|
208
208
|
this.name = 'human_operate';
|
|
209
|
-
this.description = `Use this tool when
|
|
210
|
-
Usage scenarios include:
|
|
209
|
+
this.description = `Use this tool when one of following appears:
|
|
211
210
|
1. Authentication (such as logging in, entering a verification code, etc.)
|
|
212
211
|
2. External system operations (such as uploading files, selecting a file save location, scanning documents, taking photos, paying, authorization, etc.)
|
|
212
|
+
|
|
213
|
+
NOTE: You should ONLY use this tool in the scenarios above.
|
|
214
|
+
|
|
213
215
|
When calling this tool to transfer control to the user, please explain in detail:
|
|
214
216
|
1. Why user intervention is required
|
|
215
217
|
2. What operations the user needs to perform`;
|
|
@@ -218,7 +220,7 @@ When calling this tool to transfer control to the user, please explain in detail
|
|
|
218
220
|
properties: {
|
|
219
221
|
reason: {
|
|
220
222
|
type: 'string',
|
|
221
|
-
description: 'The reason why you need to transfer control.',
|
|
223
|
+
description: 'The reason why you need to transfer control. Should follow the format: "Please ..., and click the "Completed" button to continue.".',
|
|
222
224
|
},
|
|
223
225
|
},
|
|
224
226
|
required: ['reason'],
|
package/dist/nodejs.esm.js
CHANGED
|
@@ -59,7 +59,7 @@ class HumanInputText {
|
|
|
59
59
|
properties: {
|
|
60
60
|
question: {
|
|
61
61
|
type: 'string',
|
|
62
|
-
description: 'Ask the user here.',
|
|
62
|
+
description: 'Ask the user here. Should follow the format: "Please input ...".',
|
|
63
63
|
},
|
|
64
64
|
},
|
|
65
65
|
required: ['question'],
|
|
@@ -100,7 +100,7 @@ class HumanInputSingleChoice {
|
|
|
100
100
|
properties: {
|
|
101
101
|
question: {
|
|
102
102
|
type: 'string',
|
|
103
|
-
description: 'Ask the user here.',
|
|
103
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
104
104
|
},
|
|
105
105
|
choices: {
|
|
106
106
|
type: 'array',
|
|
@@ -155,7 +155,7 @@ class HumanInputMultipleChoice {
|
|
|
155
155
|
properties: {
|
|
156
156
|
question: {
|
|
157
157
|
type: 'string',
|
|
158
|
-
description: 'Ask the user here.',
|
|
158
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
159
159
|
},
|
|
160
160
|
choices: {
|
|
161
161
|
type: 'array',
|
|
@@ -204,10 +204,12 @@ class HumanInputMultipleChoice {
|
|
|
204
204
|
class HumanOperate {
|
|
205
205
|
constructor() {
|
|
206
206
|
this.name = 'human_operate';
|
|
207
|
-
this.description = `Use this tool when
|
|
208
|
-
Usage scenarios include:
|
|
207
|
+
this.description = `Use this tool when one of following appears:
|
|
209
208
|
1. Authentication (such as logging in, entering a verification code, etc.)
|
|
210
209
|
2. External system operations (such as uploading files, selecting a file save location, scanning documents, taking photos, paying, authorization, etc.)
|
|
210
|
+
|
|
211
|
+
NOTE: You should ONLY use this tool in the scenarios above.
|
|
212
|
+
|
|
211
213
|
When calling this tool to transfer control to the user, please explain in detail:
|
|
212
214
|
1. Why user intervention is required
|
|
213
215
|
2. What operations the user needs to perform`;
|
|
@@ -216,7 +218,7 @@ When calling this tool to transfer control to the user, please explain in detail
|
|
|
216
218
|
properties: {
|
|
217
219
|
reason: {
|
|
218
220
|
type: 'string',
|
|
219
|
-
description: 'The reason why you need to transfer control.',
|
|
221
|
+
description: 'The reason why you need to transfer control. Should follow the format: "Please ..., and click the "Completed" button to continue.".',
|
|
220
222
|
},
|
|
221
223
|
},
|
|
222
224
|
required: ['reason'],
|
package/dist/web.cjs.js
CHANGED
|
@@ -9018,7 +9018,7 @@ class HumanInputText {
|
|
|
9018
9018
|
properties: {
|
|
9019
9019
|
question: {
|
|
9020
9020
|
type: 'string',
|
|
9021
|
-
description: 'Ask the user here.',
|
|
9021
|
+
description: 'Ask the user here. Should follow the format: "Please input ...".',
|
|
9022
9022
|
},
|
|
9023
9023
|
},
|
|
9024
9024
|
required: ['question'],
|
|
@@ -9059,7 +9059,7 @@ class HumanInputSingleChoice {
|
|
|
9059
9059
|
properties: {
|
|
9060
9060
|
question: {
|
|
9061
9061
|
type: 'string',
|
|
9062
|
-
description: 'Ask the user here.',
|
|
9062
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
9063
9063
|
},
|
|
9064
9064
|
choices: {
|
|
9065
9065
|
type: 'array',
|
|
@@ -9114,7 +9114,7 @@ class HumanInputMultipleChoice {
|
|
|
9114
9114
|
properties: {
|
|
9115
9115
|
question: {
|
|
9116
9116
|
type: 'string',
|
|
9117
|
-
description: 'Ask the user here.',
|
|
9117
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
9118
9118
|
},
|
|
9119
9119
|
choices: {
|
|
9120
9120
|
type: 'array',
|
|
@@ -9163,10 +9163,12 @@ class HumanInputMultipleChoice {
|
|
|
9163
9163
|
class HumanOperate {
|
|
9164
9164
|
constructor() {
|
|
9165
9165
|
this.name = 'human_operate';
|
|
9166
|
-
this.description = `Use this tool when
|
|
9167
|
-
Usage scenarios include:
|
|
9166
|
+
this.description = `Use this tool when one of following appears:
|
|
9168
9167
|
1. Authentication (such as logging in, entering a verification code, etc.)
|
|
9169
9168
|
2. External system operations (such as uploading files, selecting a file save location, scanning documents, taking photos, paying, authorization, etc.)
|
|
9169
|
+
|
|
9170
|
+
NOTE: You should ONLY use this tool in the scenarios above.
|
|
9171
|
+
|
|
9170
9172
|
When calling this tool to transfer control to the user, please explain in detail:
|
|
9171
9173
|
1. Why user intervention is required
|
|
9172
9174
|
2. What operations the user needs to perform`;
|
|
@@ -9175,7 +9177,7 @@ When calling this tool to transfer control to the user, please explain in detail
|
|
|
9175
9177
|
properties: {
|
|
9176
9178
|
reason: {
|
|
9177
9179
|
type: 'string',
|
|
9178
|
-
description: 'The reason why you need to transfer control.',
|
|
9180
|
+
description: 'The reason why you need to transfer control. Should follow the format: "Please ..., and click the "Completed" button to continue.".',
|
|
9179
9181
|
},
|
|
9180
9182
|
},
|
|
9181
9183
|
required: ['reason'],
|
package/dist/web.esm.js
CHANGED
|
@@ -9016,7 +9016,7 @@ class HumanInputText {
|
|
|
9016
9016
|
properties: {
|
|
9017
9017
|
question: {
|
|
9018
9018
|
type: 'string',
|
|
9019
|
-
description: 'Ask the user here.',
|
|
9019
|
+
description: 'Ask the user here. Should follow the format: "Please input ...".',
|
|
9020
9020
|
},
|
|
9021
9021
|
},
|
|
9022
9022
|
required: ['question'],
|
|
@@ -9057,7 +9057,7 @@ class HumanInputSingleChoice {
|
|
|
9057
9057
|
properties: {
|
|
9058
9058
|
question: {
|
|
9059
9059
|
type: 'string',
|
|
9060
|
-
description: 'Ask the user here.',
|
|
9060
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
9061
9061
|
},
|
|
9062
9062
|
choices: {
|
|
9063
9063
|
type: 'array',
|
|
@@ -9112,7 +9112,7 @@ class HumanInputMultipleChoice {
|
|
|
9112
9112
|
properties: {
|
|
9113
9113
|
question: {
|
|
9114
9114
|
type: 'string',
|
|
9115
|
-
description: 'Ask the user here.',
|
|
9115
|
+
description: 'Ask the user here. Should follow the format: "Please select ...".',
|
|
9116
9116
|
},
|
|
9117
9117
|
choices: {
|
|
9118
9118
|
type: 'array',
|
|
@@ -9161,10 +9161,12 @@ class HumanInputMultipleChoice {
|
|
|
9161
9161
|
class HumanOperate {
|
|
9162
9162
|
constructor() {
|
|
9163
9163
|
this.name = 'human_operate';
|
|
9164
|
-
this.description = `Use this tool when
|
|
9165
|
-
Usage scenarios include:
|
|
9164
|
+
this.description = `Use this tool when one of following appears:
|
|
9166
9165
|
1. Authentication (such as logging in, entering a verification code, etc.)
|
|
9167
9166
|
2. External system operations (such as uploading files, selecting a file save location, scanning documents, taking photos, paying, authorization, etc.)
|
|
9167
|
+
|
|
9168
|
+
NOTE: You should ONLY use this tool in the scenarios above.
|
|
9169
|
+
|
|
9168
9170
|
When calling this tool to transfer control to the user, please explain in detail:
|
|
9169
9171
|
1. Why user intervention is required
|
|
9170
9172
|
2. What operations the user needs to perform`;
|
|
@@ -9173,7 +9175,7 @@ When calling this tool to transfer control to the user, please explain in detail
|
|
|
9173
9175
|
properties: {
|
|
9174
9176
|
reason: {
|
|
9175
9177
|
type: 'string',
|
|
9176
|
-
description: 'The reason why you need to transfer control.',
|
|
9178
|
+
description: 'The reason why you need to transfer control. Should follow the format: "Please ..., and click the "Completed" button to continue.".',
|
|
9177
9179
|
},
|
|
9178
9180
|
},
|
|
9179
9181
|
required: ['reason'],
|