@eko-ai/eko 1.0.5 → 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 CHANGED
@@ -1,23 +1,26 @@
1
1
  # Eko
2
2
 
3
- [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://example.com/build-status) [![Version](https://img.shields.io/badge/version-0.1.0-yellow.svg)](https://example.com/version)
3
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://example.com/build-status) [![Version](https://img.shields.io/badge/version-1.0.5-yellow.svg)](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 actions using advanced AI models
10
- - **Cross-Platform Operation**: Run in browser extensions, web applications, or Node.js environments
11
- - **Powerful Tooling**: Built-in tools for browser automation, OS operations, and system control
12
- - **Flexible Integration**: Seamless integration with Claude 3.5 and other LLM models
13
- - **Developer-Friendly**: Comprehensive TypeScript support and extensive documentation
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
- ## Quick Start
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.run("Search for 'Eko framework' on Google and save the first result");
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.run("Create a new folder named 'reports' and move all PDF files there");
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
@@ -455,7 +455,6 @@ class BrowserUse {
455
455
  - Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
456
456
  - In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
457
457
  * \`input_text\`: Enter a string in the interactive element.
458
- * \`clear_text\`: Clear the text in the input/textarea element.
459
458
  * \`click\`: Click to element.
460
459
  * \`right_click\`: Right-click on the element.
461
460
  * \`double_click\`: Double-click on the element.
@@ -466,7 +465,6 @@ class BrowserUse {
466
465
  enum: [
467
466
  'screenshot_extract_element',
468
467
  'input_text',
469
- 'clear_text',
470
468
  'click',
471
469
  'right_click',
472
470
  'double_click',
@@ -519,16 +517,10 @@ class BrowserUse {
519
517
  if (params.text == null) {
520
518
  throw new Error('text parameter is required');
521
519
  }
520
+ await clear_input_by(tabId, selector_xpath, params.index);
522
521
  result = await type_by(tabId, params.text, selector_xpath, params.index);
523
522
  await sleep(200);
524
523
  break;
525
- case 'clear_text':
526
- if (params.index == null) {
527
- throw new Error('index parameter is required');
528
- }
529
- result = await clear_input_by(tabId, selector_xpath, params.index);
530
- await sleep(100);
531
- break;
532
524
  case 'click':
533
525
  if (params.index == null) {
534
526
  throw new Error('index parameter is required');
@@ -453,7 +453,6 @@ class BrowserUse {
453
453
  - Screenshots help verify element positions and relationships. Labels may sometimes overlap, so extracted elements are used to verify the correct elements.
454
454
  - In addition to screenshots, simplified information about interactive elements is returned, with element indexes corresponding to those in the screenshots.
455
455
  * \`input_text\`: Enter a string in the interactive element.
456
- * \`clear_text\`: Clear the text in the input/textarea element.
457
456
  * \`click\`: Click to element.
458
457
  * \`right_click\`: Right-click on the element.
459
458
  * \`double_click\`: Double-click on the element.
@@ -464,7 +463,6 @@ class BrowserUse {
464
463
  enum: [
465
464
  'screenshot_extract_element',
466
465
  'input_text',
467
- 'clear_text',
468
466
  'click',
469
467
  'right_click',
470
468
  'double_click',
@@ -517,16 +515,10 @@ class BrowserUse {
517
515
  if (params.text == null) {
518
516
  throw new Error('text parameter is required');
519
517
  }
518
+ await clear_input_by(tabId, selector_xpath, params.index);
520
519
  result = await type_by(tabId, params.text, selector_xpath, params.index);
521
520
  await sleep(200);
522
521
  break;
523
- case 'clear_text':
524
- if (params.index == null) {
525
- throw new Error('index parameter is required');
526
- }
527
- result = await clear_input_by(tabId, selector_xpath, params.index);
528
- await sleep(100);
529
- break;
530
522
  case 'click':
531
523
  if (params.index == null) {
532
524
  throw new Error('index parameter is required');
@@ -123,6 +123,9 @@ function type(request) {
123
123
  }
124
124
  input.focus && input.focus();
125
125
  if (!text) {
126
+ if (input.value == '') {
127
+ return true;
128
+ }
126
129
  input.value = '';
127
130
  }
128
131
  else {
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
  },
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
  },
@@ -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/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');
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');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eko-ai/eko",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Empowering language to transform human words into action.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",