@eko-ai/eko 1.0.4 → 1.0.6
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 +15 -9
- package/dist/core/eko.d.ts +1 -1
- package/dist/extension/core.d.ts +1 -1
- package/dist/extension/utils.d.ts +1 -1
- package/dist/extension.cjs.js +20 -16
- package/dist/extension.esm.js +20 -16
- package/dist/extension_content_script.js +3 -0
- package/dist/index.cjs.js +50 -3
- package/dist/index.esm.js +50 -3
- package/dist/models/workflow.d.ts +1 -0
- package/dist/nodejs/core.d.ts +1 -1
- package/dist/nodejs.cjs.js +2 -2
- package/dist/nodejs.esm.js +2 -2
- package/dist/web/core.d.ts +1 -1
- package/dist/web.cjs.js +6 -11
- package/dist/web.esm.js +6 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
# Eko
|
|
2
2
|
|
|
3
|
-
[](LICENSE) [](https://example.com/build-status) [](LICENSE) [](https://example.com/build-status) [](https://eko.fellou.ai/docs/release/versions/)
|
|
4
4
|
|
|
5
5
|
**Eko** is a revolutionary framework designed to empower developers and users alike to program their browser and operating system using natural language. With seamless integration of browser APIs, OS-level capabilities, and cutting-edge AI tools like Claude 3.5, Eko redefines how we interact with technology, making it intuitive, powerful, and accessible.
|
|
6
6
|
|
|
7
7
|
## Key Features
|
|
8
8
|
|
|
9
|
-
- **Natural Language Programming**: Transform human instructions into executable
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
9
|
+
- **Natural Language Programming**: Transform human instructions into e- **Natural Language Programming**: Convert natural language task descriptions into executable workflows, making agent development more intuitive
|
|
10
|
+
- **Two-Layer Execution Model**: Separate offline planning from online execution, making agent decisions more structured and explainable
|
|
11
|
+
- **Comprehensive Tooling**: Rich built-in tools for browser automation, computer control, file operations, and web interactions
|
|
12
|
+
- **Hybrid Drive System:** Combine LLM capabilities with developer control, enabling "human in the loop" and allowing interference at multiple levels of granularity
|
|
13
|
+
- **Event-Driven Automation:** Trigger workflows based on browser or system events
|
|
14
|
+
- **Environment Flexibility**: Work across different environments ( [Browser Extension](/docs/browseruse/browser-extension), [Web](/docs/browseruse/browser-web), [Node.js](/docs/computeruse/computer-node), [Next-Gen AI Browser Fellou](/docs/computeruse/computer-fellou) ) with consistent APIs
|
|
14
15
|
|
|
15
|
-
##
|
|
16
|
+
## Quickstart
|
|
16
17
|
|
|
17
18
|
```bash
|
|
18
19
|
npm install @eko-ai/eko
|
|
19
20
|
```
|
|
20
21
|
|
|
22
|
+
> The following code is for reference only. For detailed usage, please refer to the [Eko Quickstart guide](https://eko.fellou.ai/docs/getting-started/quickstart/).
|
|
23
|
+
|
|
21
24
|
```typescript
|
|
22
25
|
import { Eko } from '@eko-ai/eko';
|
|
23
26
|
|
|
@@ -26,10 +29,13 @@ const eko = new Eko({
|
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
// Example: Browser automation
|
|
29
|
-
await eko.
|
|
32
|
+
const extWorkflow = await eko.generate("Search for 'Eko framework' on Google and save the first result");
|
|
33
|
+
await eko.execute(extWorkflow);
|
|
30
34
|
|
|
31
35
|
// Example: System operation
|
|
32
|
-
await eko.
|
|
36
|
+
const sysWorkflow = await eko.generate("Create a new folder named 'reports' and move all PDF files there");
|
|
37
|
+
await eko.execute(sysWorkflow);
|
|
38
|
+
|
|
33
39
|
```
|
|
34
40
|
|
|
35
41
|
## Use Cases
|
package/dist/core/eko.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare class Eko {
|
|
|
7
7
|
private llmProvider;
|
|
8
8
|
private toolRegistry;
|
|
9
9
|
constructor(config: EkoConfig);
|
|
10
|
-
|
|
10
|
+
generate(prompt: string, param?: EkoInvokeParam): Promise<Workflow>;
|
|
11
11
|
execute(workflow: Workflow, callback?: WorkflowCallback): Promise<void>;
|
|
12
12
|
private getTool;
|
|
13
13
|
callTool(toolName: string, input: object, callback?: WorkflowCallback): Promise<any>;
|
package/dist/extension/core.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
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
|
-
export declare function getCurrentTabId(): Promise<number | undefined>;
|
|
4
|
+
export declare function getCurrentTabId(windowId?: number | undefined): Promise<number | undefined>;
|
|
5
5
|
export declare function open_new_tab(url: string, newWindow: boolean, windowId?: number): Promise<chrome.tabs.Tab>;
|
|
6
6
|
export declare function executeScript(tabId: number, func: any, args: any[]): Promise<any>;
|
|
7
7
|
export declare function waitForTabComplete(tabId: number, timeout?: number): Promise<chrome.tabs.Tab>;
|
package/dist/extension.cjs.js
CHANGED
|
@@ -39,24 +39,36 @@ async function getTabId(context) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
if (!tabId) {
|
|
42
|
-
|
|
42
|
+
let windowId = context.variables.get('windowId');
|
|
43
|
+
if (windowId) {
|
|
44
|
+
try {
|
|
45
|
+
tabId = await getCurrentTabId(windowId);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
tabId = await getCurrentTabId();
|
|
49
|
+
context.variables.delete('windowId');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
tabId = await getCurrentTabId();
|
|
54
|
+
}
|
|
43
55
|
}
|
|
44
56
|
return tabId;
|
|
45
57
|
}
|
|
46
|
-
function getCurrentTabId() {
|
|
58
|
+
function getCurrentTabId(windowId) {
|
|
47
59
|
return new Promise((resolve) => {
|
|
48
|
-
chrome.tabs.query({ active: true, lastFocusedWindow: true }, function (tabs) {
|
|
60
|
+
chrome.tabs.query({ windowId, active: true, lastFocusedWindow: true }, function (tabs) {
|
|
49
61
|
if (tabs.length > 0) {
|
|
50
62
|
resolve(tabs[0].id);
|
|
51
63
|
}
|
|
52
64
|
else {
|
|
53
|
-
chrome.tabs.query({ active: true, currentWindow: true }, function (_tabs) {
|
|
65
|
+
chrome.tabs.query({ windowId, active: true, currentWindow: true }, function (_tabs) {
|
|
54
66
|
if (_tabs.length > 0) {
|
|
55
67
|
resolve(_tabs[0].id);
|
|
56
68
|
return;
|
|
57
69
|
}
|
|
58
70
|
else {
|
|
59
|
-
chrome.tabs.query({ status: 'complete', currentWindow: true }, function (__tabs) {
|
|
71
|
+
chrome.tabs.query({ windowId, status: 'complete', currentWindow: true }, function (__tabs) {
|
|
60
72
|
resolve(__tabs.length ? __tabs[__tabs.length - 1].id : undefined);
|
|
61
73
|
});
|
|
62
74
|
}
|
|
@@ -443,7 +455,6 @@ class BrowserUse {
|
|
|
443
455
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
444
456
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
445
457
|
* \`input_text\`: Enter a string in the interactive element.
|
|
446
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
447
458
|
* \`click\`: Click to element.
|
|
448
459
|
* \`right_click\`: Right-click on the element.
|
|
449
460
|
* \`double_click\`: Double-click on the element.
|
|
@@ -454,7 +465,6 @@ class BrowserUse {
|
|
|
454
465
|
enum: [
|
|
455
466
|
'screenshot_extract_element',
|
|
456
467
|
'input_text',
|
|
457
|
-
'clear_text',
|
|
458
468
|
'click',
|
|
459
469
|
'right_click',
|
|
460
470
|
'double_click',
|
|
@@ -507,16 +517,10 @@ class BrowserUse {
|
|
|
507
517
|
if (params.text == null) {
|
|
508
518
|
throw new Error('text parameter is required');
|
|
509
519
|
}
|
|
520
|
+
await clear_input_by(tabId, selector_xpath, params.index);
|
|
510
521
|
result = await type_by(tabId, params.text, selector_xpath, params.index);
|
|
511
522
|
await sleep(200);
|
|
512
523
|
break;
|
|
513
|
-
case 'clear_text':
|
|
514
|
-
if (params.index == null) {
|
|
515
|
-
throw new Error('index parameter is required');
|
|
516
|
-
}
|
|
517
|
-
result = await clear_input_by(tabId, selector_xpath, params.index);
|
|
518
|
-
await sleep(100);
|
|
519
|
-
break;
|
|
520
524
|
case 'click':
|
|
521
525
|
if (params.index == null) {
|
|
522
526
|
throw new Error('index parameter is required');
|
|
@@ -1663,7 +1667,7 @@ async function getLLMConfig(name = 'llmConfig') {
|
|
|
1663
1667
|
let result = await chrome.storage.sync.get([name]);
|
|
1664
1668
|
return result[name];
|
|
1665
1669
|
}
|
|
1666
|
-
function
|
|
1670
|
+
function loadTools() {
|
|
1667
1671
|
let toolsMap = new Map();
|
|
1668
1672
|
for (const key in tools) {
|
|
1669
1673
|
let tool = tools[key];
|
|
@@ -1681,8 +1685,8 @@ function getAllTools() {
|
|
|
1681
1685
|
}
|
|
1682
1686
|
|
|
1683
1687
|
exports.browser = browser;
|
|
1684
|
-
exports.getAllTools = getAllTools;
|
|
1685
1688
|
exports.getLLMConfig = getLLMConfig;
|
|
1689
|
+
exports.loadTools = loadTools;
|
|
1686
1690
|
exports.pub = pub;
|
|
1687
1691
|
exports.tools = tools;
|
|
1688
1692
|
exports.utils = utils;
|
package/dist/extension.esm.js
CHANGED
|
@@ -37,24 +37,36 @@ async function getTabId(context) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
if (!tabId) {
|
|
40
|
-
|
|
40
|
+
let windowId = context.variables.get('windowId');
|
|
41
|
+
if (windowId) {
|
|
42
|
+
try {
|
|
43
|
+
tabId = await getCurrentTabId(windowId);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
tabId = await getCurrentTabId();
|
|
47
|
+
context.variables.delete('windowId');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
tabId = await getCurrentTabId();
|
|
52
|
+
}
|
|
41
53
|
}
|
|
42
54
|
return tabId;
|
|
43
55
|
}
|
|
44
|
-
function getCurrentTabId() {
|
|
56
|
+
function getCurrentTabId(windowId) {
|
|
45
57
|
return new Promise((resolve) => {
|
|
46
|
-
chrome.tabs.query({ active: true, lastFocusedWindow: true }, function (tabs) {
|
|
58
|
+
chrome.tabs.query({ windowId, active: true, lastFocusedWindow: true }, function (tabs) {
|
|
47
59
|
if (tabs.length > 0) {
|
|
48
60
|
resolve(tabs[0].id);
|
|
49
61
|
}
|
|
50
62
|
else {
|
|
51
|
-
chrome.tabs.query({ active: true, currentWindow: true }, function (_tabs) {
|
|
63
|
+
chrome.tabs.query({ windowId, active: true, currentWindow: true }, function (_tabs) {
|
|
52
64
|
if (_tabs.length > 0) {
|
|
53
65
|
resolve(_tabs[0].id);
|
|
54
66
|
return;
|
|
55
67
|
}
|
|
56
68
|
else {
|
|
57
|
-
chrome.tabs.query({ status: 'complete', currentWindow: true }, function (__tabs) {
|
|
69
|
+
chrome.tabs.query({ windowId, status: 'complete', currentWindow: true }, function (__tabs) {
|
|
58
70
|
resolve(__tabs.length ? __tabs[__tabs.length - 1].id : undefined);
|
|
59
71
|
});
|
|
60
72
|
}
|
|
@@ -441,7 +453,6 @@ class BrowserUse {
|
|
|
441
453
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
442
454
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
443
455
|
* \`input_text\`: Enter a string in the interactive element.
|
|
444
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
445
456
|
* \`click\`: Click to element.
|
|
446
457
|
* \`right_click\`: Right-click on the element.
|
|
447
458
|
* \`double_click\`: Double-click on the element.
|
|
@@ -452,7 +463,6 @@ class BrowserUse {
|
|
|
452
463
|
enum: [
|
|
453
464
|
'screenshot_extract_element',
|
|
454
465
|
'input_text',
|
|
455
|
-
'clear_text',
|
|
456
466
|
'click',
|
|
457
467
|
'right_click',
|
|
458
468
|
'double_click',
|
|
@@ -505,16 +515,10 @@ class BrowserUse {
|
|
|
505
515
|
if (params.text == null) {
|
|
506
516
|
throw new Error('text parameter is required');
|
|
507
517
|
}
|
|
518
|
+
await clear_input_by(tabId, selector_xpath, params.index);
|
|
508
519
|
result = await type_by(tabId, params.text, selector_xpath, params.index);
|
|
509
520
|
await sleep(200);
|
|
510
521
|
break;
|
|
511
|
-
case 'clear_text':
|
|
512
|
-
if (params.index == null) {
|
|
513
|
-
throw new Error('index parameter is required');
|
|
514
|
-
}
|
|
515
|
-
result = await clear_input_by(tabId, selector_xpath, params.index);
|
|
516
|
-
await sleep(100);
|
|
517
|
-
break;
|
|
518
522
|
case 'click':
|
|
519
523
|
if (params.index == null) {
|
|
520
524
|
throw new Error('index parameter is required');
|
|
@@ -1661,7 +1665,7 @@ async function getLLMConfig(name = 'llmConfig') {
|
|
|
1661
1665
|
let result = await chrome.storage.sync.get([name]);
|
|
1662
1666
|
return result[name];
|
|
1663
1667
|
}
|
|
1664
|
-
function
|
|
1668
|
+
function loadTools() {
|
|
1665
1669
|
let toolsMap = new Map();
|
|
1666
1670
|
for (const key in tools) {
|
|
1667
1671
|
let tool = tools[key];
|
|
@@ -1678,4 +1682,4 @@ function getAllTools() {
|
|
|
1678
1682
|
return toolsMap;
|
|
1679
1683
|
}
|
|
1680
1684
|
|
|
1681
|
-
export { browser,
|
|
1685
|
+
export { browser, getLLMConfig, loadTools, pub, tools, utils };
|
package/dist/index.cjs.js
CHANGED
|
@@ -16,11 +16,15 @@ class WorkflowImpl {
|
|
|
16
16
|
if (!this.validateDAG()) {
|
|
17
17
|
throw new Error("Invalid workflow: Contains circular dependencies");
|
|
18
18
|
}
|
|
19
|
+
this.abort = false;
|
|
19
20
|
callback && await ((_b = (_a = callback.hooks).beforeWorkflow) === null || _b === void 0 ? void 0 : _b.call(_a, this));
|
|
20
21
|
const executed = new Set();
|
|
21
22
|
const executing = new Set();
|
|
22
23
|
const executeNode = async (nodeId) => {
|
|
23
24
|
var _a, _b, _c, _d, _e;
|
|
25
|
+
if (this.abort) {
|
|
26
|
+
throw new Error("Abort");
|
|
27
|
+
}
|
|
24
28
|
if (executed.has(nodeId)) {
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
@@ -30,12 +34,22 @@ class WorkflowImpl {
|
|
|
30
34
|
const node = this.getNode(nodeId);
|
|
31
35
|
// Execute the node's action
|
|
32
36
|
const context = {
|
|
37
|
+
__skip: false,
|
|
38
|
+
__abort: false,
|
|
33
39
|
variables: this.variables,
|
|
34
40
|
llmProvider: this.llmProvider,
|
|
35
41
|
tools: new Map(node.action.tools.map(tool => [tool.name, tool])),
|
|
36
|
-
callback
|
|
42
|
+
callback,
|
|
43
|
+
next: () => context.__skip = true,
|
|
44
|
+
abortAll: () => this.abort = context.__abort = true,
|
|
37
45
|
};
|
|
38
46
|
callback && await ((_b = (_a = callback.hooks).beforeSubtask) === null || _b === void 0 ? void 0 : _b.call(_a, node, context));
|
|
47
|
+
if (context.__abort) {
|
|
48
|
+
throw new Error("Abort");
|
|
49
|
+
}
|
|
50
|
+
else if (context.__skip) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
39
53
|
executing.add(nodeId);
|
|
40
54
|
// Execute dependencies first
|
|
41
55
|
for (const depId of node.dependencies) {
|
|
@@ -221,12 +235,16 @@ class ActionImpl {
|
|
|
221
235
|
toolExecutionPromise = (async () => {
|
|
222
236
|
try {
|
|
223
237
|
// beforeToolUse
|
|
238
|
+
context.__skip = false;
|
|
224
239
|
if (context.callback && context.callback.hooks.beforeToolUse) {
|
|
225
240
|
let modified_input = await context.callback.hooks.beforeToolUse(tool, context, toolCall.input);
|
|
226
241
|
if (modified_input) {
|
|
227
242
|
toolCall.input = modified_input;
|
|
228
243
|
}
|
|
229
244
|
}
|
|
245
|
+
if (context.__skip || context.__abort) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
230
248
|
// Execute the tool
|
|
231
249
|
let result = await tool.execute(context, toolCall.input);
|
|
232
250
|
// afterToolUse
|
|
@@ -291,6 +309,9 @@ class ActionImpl {
|
|
|
291
309
|
if (toolExecutionPromise) {
|
|
292
310
|
await toolExecutionPromise;
|
|
293
311
|
}
|
|
312
|
+
if (context.__abort) {
|
|
313
|
+
throw new Error("Abort");
|
|
314
|
+
}
|
|
294
315
|
// Add messages in the correct order after everything is complete
|
|
295
316
|
if (assistantTextMessage) {
|
|
296
317
|
roundMessages.push({ role: 'assistant', content: assistantTextMessage });
|
|
@@ -440,7 +461,7 @@ ${toolDescriptions}
|
|
|
440
461
|
Generate a complete workflow that:
|
|
441
462
|
1. Only uses the tools listed above
|
|
442
463
|
2. Properly sequences tool usage based on dependencies
|
|
443
|
-
3. Ensures each action has appropriate input/output schemas
|
|
464
|
+
3. Ensures each action has appropriate input/output schemas, and that the "tools" field in each action is populated with the sufficient subset of all available tools needed to complete the action
|
|
444
465
|
4. Creates a clear, logical flow to accomplish the user's goal
|
|
445
466
|
5. Includes detailed descriptions for each action, ensuring that the actions, when combined, is a complete solution to the user's problem`;
|
|
446
467
|
},
|
|
@@ -3289,6 +3310,19 @@ class ClaudeProvider {
|
|
|
3289
3310
|
if (defaultModel) {
|
|
3290
3311
|
this.defaultModel = defaultModel;
|
|
3291
3312
|
}
|
|
3313
|
+
if (typeof window !== 'undefined' &&
|
|
3314
|
+
typeof document !== 'undefined' &&
|
|
3315
|
+
(typeof param == 'string' || param.apiKey)) {
|
|
3316
|
+
console.warn(`
|
|
3317
|
+
⚠️ Security Warning:
|
|
3318
|
+
DO NOT use API Keys in browser/frontend code!
|
|
3319
|
+
This will expose your credentials and may lead to unauthorized usage.
|
|
3320
|
+
|
|
3321
|
+
Best Practices: Configure backend API proxy request through baseURL and request headers.
|
|
3322
|
+
|
|
3323
|
+
Please refer to the link: https://eko.fellou.ai/docs/getting-started/configuration#web-environment
|
|
3324
|
+
`);
|
|
3325
|
+
}
|
|
3292
3326
|
if (typeof param == 'string') {
|
|
3293
3327
|
this.client = new Anthropic({
|
|
3294
3328
|
apiKey: param,
|
|
@@ -8696,6 +8730,19 @@ class OpenaiProvider {
|
|
|
8696
8730
|
if (defaultModel) {
|
|
8697
8731
|
this.defaultModel = defaultModel;
|
|
8698
8732
|
}
|
|
8733
|
+
if (typeof window !== 'undefined' &&
|
|
8734
|
+
typeof document !== 'undefined' &&
|
|
8735
|
+
(typeof param == 'string' || param.apiKey)) {
|
|
8736
|
+
console.warn(`
|
|
8737
|
+
⚠️ Security Warning:
|
|
8738
|
+
DO NOT use API Keys in browser/frontend code!
|
|
8739
|
+
This will expose your credentials and may lead to unauthorized usage.
|
|
8740
|
+
|
|
8741
|
+
Best Practices: Configure backend API proxy request through baseURL and request headers.
|
|
8742
|
+
|
|
8743
|
+
Please refer to the link: https://eko.fellou.ai/docs/getting-started/configuration#web-environment
|
|
8744
|
+
`);
|
|
8745
|
+
}
|
|
8699
8746
|
if (typeof param == 'string') {
|
|
8700
8747
|
this.client = new OpenAI({
|
|
8701
8748
|
apiKey: param,
|
|
@@ -9086,7 +9133,7 @@ class Eko {
|
|
|
9086
9133
|
}
|
|
9087
9134
|
Eko.tools.forEach((tool) => this.toolRegistry.registerTool(tool));
|
|
9088
9135
|
}
|
|
9089
|
-
async
|
|
9136
|
+
async generate(prompt, param) {
|
|
9090
9137
|
let toolRegistry = this.toolRegistry;
|
|
9091
9138
|
if (param && param.tools && param.tools.length > 0) {
|
|
9092
9139
|
toolRegistry = new ToolRegistry();
|
package/dist/index.esm.js
CHANGED
|
@@ -12,11 +12,15 @@ class WorkflowImpl {
|
|
|
12
12
|
if (!this.validateDAG()) {
|
|
13
13
|
throw new Error("Invalid workflow: Contains circular dependencies");
|
|
14
14
|
}
|
|
15
|
+
this.abort = false;
|
|
15
16
|
callback && await ((_b = (_a = callback.hooks).beforeWorkflow) === null || _b === void 0 ? void 0 : _b.call(_a, this));
|
|
16
17
|
const executed = new Set();
|
|
17
18
|
const executing = new Set();
|
|
18
19
|
const executeNode = async (nodeId) => {
|
|
19
20
|
var _a, _b, _c, _d, _e;
|
|
21
|
+
if (this.abort) {
|
|
22
|
+
throw new Error("Abort");
|
|
23
|
+
}
|
|
20
24
|
if (executed.has(nodeId)) {
|
|
21
25
|
return;
|
|
22
26
|
}
|
|
@@ -26,12 +30,22 @@ class WorkflowImpl {
|
|
|
26
30
|
const node = this.getNode(nodeId);
|
|
27
31
|
// Execute the node's action
|
|
28
32
|
const context = {
|
|
33
|
+
__skip: false,
|
|
34
|
+
__abort: false,
|
|
29
35
|
variables: this.variables,
|
|
30
36
|
llmProvider: this.llmProvider,
|
|
31
37
|
tools: new Map(node.action.tools.map(tool => [tool.name, tool])),
|
|
32
|
-
callback
|
|
38
|
+
callback,
|
|
39
|
+
next: () => context.__skip = true,
|
|
40
|
+
abortAll: () => this.abort = context.__abort = true,
|
|
33
41
|
};
|
|
34
42
|
callback && await ((_b = (_a = callback.hooks).beforeSubtask) === null || _b === void 0 ? void 0 : _b.call(_a, node, context));
|
|
43
|
+
if (context.__abort) {
|
|
44
|
+
throw new Error("Abort");
|
|
45
|
+
}
|
|
46
|
+
else if (context.__skip) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
35
49
|
executing.add(nodeId);
|
|
36
50
|
// Execute dependencies first
|
|
37
51
|
for (const depId of node.dependencies) {
|
|
@@ -217,12 +231,16 @@ class ActionImpl {
|
|
|
217
231
|
toolExecutionPromise = (async () => {
|
|
218
232
|
try {
|
|
219
233
|
// beforeToolUse
|
|
234
|
+
context.__skip = false;
|
|
220
235
|
if (context.callback && context.callback.hooks.beforeToolUse) {
|
|
221
236
|
let modified_input = await context.callback.hooks.beforeToolUse(tool, context, toolCall.input);
|
|
222
237
|
if (modified_input) {
|
|
223
238
|
toolCall.input = modified_input;
|
|
224
239
|
}
|
|
225
240
|
}
|
|
241
|
+
if (context.__skip || context.__abort) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
226
244
|
// Execute the tool
|
|
227
245
|
let result = await tool.execute(context, toolCall.input);
|
|
228
246
|
// afterToolUse
|
|
@@ -287,6 +305,9 @@ class ActionImpl {
|
|
|
287
305
|
if (toolExecutionPromise) {
|
|
288
306
|
await toolExecutionPromise;
|
|
289
307
|
}
|
|
308
|
+
if (context.__abort) {
|
|
309
|
+
throw new Error("Abort");
|
|
310
|
+
}
|
|
290
311
|
// Add messages in the correct order after everything is complete
|
|
291
312
|
if (assistantTextMessage) {
|
|
292
313
|
roundMessages.push({ role: 'assistant', content: assistantTextMessage });
|
|
@@ -436,7 +457,7 @@ ${toolDescriptions}
|
|
|
436
457
|
Generate a complete workflow that:
|
|
437
458
|
1. Only uses the tools listed above
|
|
438
459
|
2. Properly sequences tool usage based on dependencies
|
|
439
|
-
3. Ensures each action has appropriate input/output schemas
|
|
460
|
+
3. Ensures each action has appropriate input/output schemas, and that the "tools" field in each action is populated with the sufficient subset of all available tools needed to complete the action
|
|
440
461
|
4. Creates a clear, logical flow to accomplish the user's goal
|
|
441
462
|
5. Includes detailed descriptions for each action, ensuring that the actions, when combined, is a complete solution to the user's problem`;
|
|
442
463
|
},
|
|
@@ -3285,6 +3306,19 @@ class ClaudeProvider {
|
|
|
3285
3306
|
if (defaultModel) {
|
|
3286
3307
|
this.defaultModel = defaultModel;
|
|
3287
3308
|
}
|
|
3309
|
+
if (typeof window !== 'undefined' &&
|
|
3310
|
+
typeof document !== 'undefined' &&
|
|
3311
|
+
(typeof param == 'string' || param.apiKey)) {
|
|
3312
|
+
console.warn(`
|
|
3313
|
+
⚠️ Security Warning:
|
|
3314
|
+
DO NOT use API Keys in browser/frontend code!
|
|
3315
|
+
This will expose your credentials and may lead to unauthorized usage.
|
|
3316
|
+
|
|
3317
|
+
Best Practices: Configure backend API proxy request through baseURL and request headers.
|
|
3318
|
+
|
|
3319
|
+
Please refer to the link: https://eko.fellou.ai/docs/getting-started/configuration#web-environment
|
|
3320
|
+
`);
|
|
3321
|
+
}
|
|
3288
3322
|
if (typeof param == 'string') {
|
|
3289
3323
|
this.client = new Anthropic({
|
|
3290
3324
|
apiKey: param,
|
|
@@ -8692,6 +8726,19 @@ class OpenaiProvider {
|
|
|
8692
8726
|
if (defaultModel) {
|
|
8693
8727
|
this.defaultModel = defaultModel;
|
|
8694
8728
|
}
|
|
8729
|
+
if (typeof window !== 'undefined' &&
|
|
8730
|
+
typeof document !== 'undefined' &&
|
|
8731
|
+
(typeof param == 'string' || param.apiKey)) {
|
|
8732
|
+
console.warn(`
|
|
8733
|
+
⚠️ Security Warning:
|
|
8734
|
+
DO NOT use API Keys in browser/frontend code!
|
|
8735
|
+
This will expose your credentials and may lead to unauthorized usage.
|
|
8736
|
+
|
|
8737
|
+
Best Practices: Configure backend API proxy request through baseURL and request headers.
|
|
8738
|
+
|
|
8739
|
+
Please refer to the link: https://eko.fellou.ai/docs/getting-started/configuration#web-environment
|
|
8740
|
+
`);
|
|
8741
|
+
}
|
|
8695
8742
|
if (typeof param == 'string') {
|
|
8696
8743
|
this.client = new OpenAI({
|
|
8697
8744
|
apiKey: param,
|
|
@@ -9082,7 +9129,7 @@ class Eko {
|
|
|
9082
9129
|
}
|
|
9083
9130
|
Eko.tools.forEach((tool) => this.toolRegistry.registerTool(tool));
|
|
9084
9131
|
}
|
|
9085
|
-
async
|
|
9132
|
+
async generate(prompt, param) {
|
|
9086
9133
|
let toolRegistry = this.toolRegistry;
|
|
9087
9134
|
if (param && param.tools && param.tools.length > 0) {
|
|
9088
9135
|
toolRegistry = new ToolRegistry();
|
|
@@ -6,6 +6,7 @@ export declare class WorkflowImpl implements Workflow {
|
|
|
6
6
|
nodes: WorkflowNode[];
|
|
7
7
|
variables: Map<string, unknown>;
|
|
8
8
|
llmProvider?: LLMProvider | undefined;
|
|
9
|
+
abort?: boolean;
|
|
9
10
|
constructor(id: string, name: string, description?: string | undefined, nodes?: WorkflowNode[], variables?: Map<string, unknown>, llmProvider?: LLMProvider | undefined);
|
|
10
11
|
execute(callback?: WorkflowCallback): Promise<void>;
|
|
11
12
|
addNode(node: WorkflowNode): void;
|
package/dist/nodejs/core.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Tool } from '../types';
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function loadTools(): Map<string, Tool<any, any>>;
|
package/dist/nodejs.cjs.js
CHANGED
|
@@ -210,7 +210,7 @@ var tools = /*#__PURE__*/Object.freeze({
|
|
|
210
210
|
FileWrite: FileWrite
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
-
function
|
|
213
|
+
function loadTools() {
|
|
214
214
|
let toolsMap = new Map();
|
|
215
215
|
for (const key in tools) {
|
|
216
216
|
let tool = tools[key];
|
|
@@ -227,5 +227,5 @@ function getAllTools() {
|
|
|
227
227
|
return toolsMap;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
exports.
|
|
230
|
+
exports.loadTools = loadTools;
|
|
231
231
|
exports.tools = tools;
|
package/dist/nodejs.esm.js
CHANGED
|
@@ -208,7 +208,7 @@ var tools = /*#__PURE__*/Object.freeze({
|
|
|
208
208
|
FileWrite: FileWrite
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
-
function
|
|
211
|
+
function loadTools() {
|
|
212
212
|
let toolsMap = new Map();
|
|
213
213
|
for (const key in tools) {
|
|
214
214
|
let tool = tools[key];
|
|
@@ -225,4 +225,4 @@ function getAllTools() {
|
|
|
225
225
|
return toolsMap;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
export {
|
|
228
|
+
export { loadTools, tools };
|
package/dist/web/core.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Tool } from '../types';
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function loadTools(): Map<string, Tool<any, any>>;
|
package/dist/web.cjs.js
CHANGED
|
@@ -8581,6 +8581,9 @@ function do_input(text, xpath, highlightIndex) {
|
|
|
8581
8581
|
}
|
|
8582
8582
|
input.focus && input.focus();
|
|
8583
8583
|
if (!text) {
|
|
8584
|
+
if (input.value == '') {
|
|
8585
|
+
return true;
|
|
8586
|
+
}
|
|
8584
8587
|
input.value = '';
|
|
8585
8588
|
}
|
|
8586
8589
|
else {
|
|
@@ -8682,7 +8685,6 @@ class BrowserUse {
|
|
|
8682
8685
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
8683
8686
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
8684
8687
|
* \`input_text\`: Enter a string in the interactive element.
|
|
8685
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
8686
8688
|
* \`click\`: Click to element.
|
|
8687
8689
|
* \`right_click\`: Right-click on the element.
|
|
8688
8690
|
* \`double_click\`: Double-click on the element.
|
|
@@ -8693,7 +8695,6 @@ class BrowserUse {
|
|
|
8693
8695
|
enum: [
|
|
8694
8696
|
'screenshot_extract_element',
|
|
8695
8697
|
'input_text',
|
|
8696
|
-
'clear_text',
|
|
8697
8698
|
'click',
|
|
8698
8699
|
'right_click',
|
|
8699
8700
|
'double_click',
|
|
@@ -8744,16 +8745,10 @@ class BrowserUse {
|
|
|
8744
8745
|
if (params.text == null) {
|
|
8745
8746
|
throw new Error('text parameter is required');
|
|
8746
8747
|
}
|
|
8748
|
+
await clear_input(selector_xpath, params.index);
|
|
8747
8749
|
result = await type(params.text, selector_xpath, params.index);
|
|
8748
8750
|
await sleep(200);
|
|
8749
8751
|
break;
|
|
8750
|
-
case 'clear_text':
|
|
8751
|
-
if (params.index == null) {
|
|
8752
|
-
throw new Error('index parameter is required');
|
|
8753
|
-
}
|
|
8754
|
-
result = await clear_input(selector_xpath, params.index);
|
|
8755
|
-
await sleep(100);
|
|
8756
|
-
break;
|
|
8757
8752
|
case 'click':
|
|
8758
8753
|
if (params.index == null) {
|
|
8759
8754
|
throw new Error('index parameter is required');
|
|
@@ -9375,7 +9370,7 @@ var tools = /*#__PURE__*/Object.freeze({
|
|
|
9375
9370
|
Screenshot: Screenshot
|
|
9376
9371
|
});
|
|
9377
9372
|
|
|
9378
|
-
function
|
|
9373
|
+
function loadTools() {
|
|
9379
9374
|
let toolsMap = new Map();
|
|
9380
9375
|
for (const key in tools) {
|
|
9381
9376
|
let tool = tools[key];
|
|
@@ -9393,5 +9388,5 @@ function getAllTools() {
|
|
|
9393
9388
|
}
|
|
9394
9389
|
|
|
9395
9390
|
exports.browser = browser;
|
|
9396
|
-
exports.
|
|
9391
|
+
exports.loadTools = loadTools;
|
|
9397
9392
|
exports.tools = tools;
|
package/dist/web.esm.js
CHANGED
|
@@ -8579,6 +8579,9 @@ function do_input(text, xpath, highlightIndex) {
|
|
|
8579
8579
|
}
|
|
8580
8580
|
input.focus && input.focus();
|
|
8581
8581
|
if (!text) {
|
|
8582
|
+
if (input.value == '') {
|
|
8583
|
+
return true;
|
|
8584
|
+
}
|
|
8582
8585
|
input.value = '';
|
|
8583
8586
|
}
|
|
8584
8587
|
else {
|
|
@@ -8680,7 +8683,6 @@ class BrowserUse {
|
|
|
8680
8683
|
- Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
|
|
8681
8684
|
- In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
|
|
8682
8685
|
* \`input_text\`: Enter a string in the interactive element.
|
|
8683
|
-
* \`clear_text\`: Clear the text in the input/textarea element.
|
|
8684
8686
|
* \`click\`: Click to element.
|
|
8685
8687
|
* \`right_click\`: Right-click on the element.
|
|
8686
8688
|
* \`double_click\`: Double-click on the element.
|
|
@@ -8691,7 +8693,6 @@ class BrowserUse {
|
|
|
8691
8693
|
enum: [
|
|
8692
8694
|
'screenshot_extract_element',
|
|
8693
8695
|
'input_text',
|
|
8694
|
-
'clear_text',
|
|
8695
8696
|
'click',
|
|
8696
8697
|
'right_click',
|
|
8697
8698
|
'double_click',
|
|
@@ -8742,16 +8743,10 @@ class BrowserUse {
|
|
|
8742
8743
|
if (params.text == null) {
|
|
8743
8744
|
throw new Error('text parameter is required');
|
|
8744
8745
|
}
|
|
8746
|
+
await clear_input(selector_xpath, params.index);
|
|
8745
8747
|
result = await type(params.text, selector_xpath, params.index);
|
|
8746
8748
|
await sleep(200);
|
|
8747
8749
|
break;
|
|
8748
|
-
case 'clear_text':
|
|
8749
|
-
if (params.index == null) {
|
|
8750
|
-
throw new Error('index parameter is required');
|
|
8751
|
-
}
|
|
8752
|
-
result = await clear_input(selector_xpath, params.index);
|
|
8753
|
-
await sleep(100);
|
|
8754
|
-
break;
|
|
8755
8750
|
case 'click':
|
|
8756
8751
|
if (params.index == null) {
|
|
8757
8752
|
throw new Error('index parameter is required');
|
|
@@ -9373,7 +9368,7 @@ var tools = /*#__PURE__*/Object.freeze({
|
|
|
9373
9368
|
Screenshot: Screenshot
|
|
9374
9369
|
});
|
|
9375
9370
|
|
|
9376
|
-
function
|
|
9371
|
+
function loadTools() {
|
|
9377
9372
|
let toolsMap = new Map();
|
|
9378
9373
|
for (const key in tools) {
|
|
9379
9374
|
let tool = tools[key];
|
|
@@ -9390,4 +9385,4 @@ function getAllTools() {
|
|
|
9390
9385
|
return toolsMap;
|
|
9391
9386
|
}
|
|
9392
9387
|
|
|
9393
|
-
export { browser,
|
|
9388
|
+
export { browser, loadTools, tools };
|