ff-automationv2 1.0.0 → 2.0.1-beta.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.
Files changed (60) hide show
  1. package/bitbucket-pipelines.yml +20 -0
  2. package/eslint.config.ts +29 -29
  3. package/package.json +51 -51
  4. package/src/ai/llmcalls/decodeApiKey.ts +14 -14
  5. package/src/ai/llmcalls/llmAction.ts +89 -89
  6. package/src/ai/llmcalls/parseLlmOputput.ts +69 -69
  7. package/src/ai/llmprompts/systemPrompts/actionExtractorPrompt.ts +70 -70
  8. package/src/ai/llmprompts/systemPrompts/errorDescriptionPrompt.ts +23 -23
  9. package/src/ai/llmprompts/systemPrompts/fireflinkElementIndexExtactors.ts +198 -198
  10. package/src/ai/llmprompts/systemPrompts/userStoryToListPrompt.ts +24 -24
  11. package/src/ai/llmprompts/systemPrompts/visionPrompt.ts +28 -28
  12. package/src/automation/actions/executor.ts +74 -74
  13. package/src/automation/actions/interaction/enterInput.ts +27 -27
  14. package/src/automation/actions/interface/interactionActionInterface.ts +26 -26
  15. package/src/automation/actions/interface/navigationActionInterface.ts +21 -21
  16. package/src/automation/actions/interface/waitActionInterface.ts +5 -5
  17. package/src/automation/actions/navigation/getTitle.ts +8 -8
  18. package/src/automation/actions/navigation/goBack.ts +8 -8
  19. package/src/automation/actions/navigation/navigate.ts +9 -9
  20. package/src/automation/actions/navigation/refresh.ts +8 -8
  21. package/src/automation/actions/wait/wait.ts +9 -9
  22. package/src/automation/browserSession/initiateBrowserSession.ts +81 -81
  23. package/src/core/constants/supportedActions.ts +7 -7
  24. package/src/core/interfaces/StableDomInterface.ts +5 -5
  25. package/src/core/interfaces/actionInterface.ts +13 -13
  26. package/src/core/interfaces/automationRunnerInterface.ts +2 -2
  27. package/src/core/interfaces/browserCapabilitiesInterface.ts +4 -4
  28. package/src/core/interfaces/browserConfigurationInterface.ts +2 -2
  29. package/src/core/interfaces/domAnalysisInterface.ts +34 -34
  30. package/src/core/interfaces/executionDetails.ts +29 -29
  31. package/src/core/interfaces/fireflinkScriptPayloadInterface.ts +39 -39
  32. package/src/core/interfaces/llmConfigurationInterface.ts +2 -2
  33. package/src/core/interfaces/llmResponseInterface.ts +38 -38
  34. package/src/core/interfaces/promptInterface.ts +21 -21
  35. package/src/core/interfaces/scriptGenrationDataInterface.ts +16 -16
  36. package/src/core/interfaces/toolsInterface.ts +5 -5
  37. package/src/core/main/actionHandlerFactory.ts +86 -86
  38. package/src/core/main/executionContext.ts +18 -18
  39. package/src/core/main/runAutomationScript.ts +177 -177
  40. package/src/core/main/stepProcessor.ts +28 -28
  41. package/src/core/types/llmResponseType.ts +10 -10
  42. package/src/core/types/visionllmInputType.ts +4 -4
  43. package/src/domAnalysis/getRelaventElements.ts +24 -24
  44. package/src/domAnalysis/relativeElementsFromDom.ts +94 -94
  45. package/src/domAnalysis/searchBest.ts +159 -159
  46. package/src/domAnalysis/simplifyAndFlatten.ts +118 -118
  47. package/src/fireflinkData/fireflinkLocators/elementsFromHTML.ts +656 -656
  48. package/src/fireflinkData/fireflinkLocators/getListOfLocators.ts +31 -31
  49. package/src/fireflinkData/fireflinkLocators/typeList.ts +36 -36
  50. package/src/fireflinkData/fireflinkScript/scriptGenrationData.ts +30 -30
  51. package/src/index.ts +5 -5
  52. package/src/llmConfig/llmConfiguration.ts +26 -26
  53. package/src/service/fireflinkApi.service.ts +46 -46
  54. package/src/service/scriptRunner.service.ts +83 -83
  55. package/src/utils/DomExtraction/jsForAttributeInjection.ts +254 -254
  56. package/src/utils/javascript/jsFindElement.ts +161 -161
  57. package/src/utils/javascript/jsForShadowRoot.ts +216 -216
  58. package/src/utils/javascript/jsForToaster.ts +60 -60
  59. package/src/utils/logger/logData.ts +36 -36
  60. package/tsconfig.json +26 -26
@@ -1,177 +1,177 @@
1
- import { IAutomationRunner } from "../interfaces/automationRunnerInterface.js";
2
- import { AutomationRequest } from "../../core/interfaces/executionDetails.js";
3
- import { ServiceProviderBaseUrlProvider } from "../../llmConfig/llmConfiguration.js";
4
- import { llmAction } from "../../ai/llmcalls/llmAction.js";
5
- import { ExecutionContext } from "./executionContext.js";
6
- import { createActionHandlers } from "../../core/main/actionHandlerFactory.js";
7
- import { StepProcessor } from "./stepProcessor.js";
8
- import { FireFlinkApiService } from "../../service/fireflinkApi.service.js";
9
- import { ScriptRunner } from "../../service/scriptRunner.service.js";
10
- import { IPayload } from "../interfaces/fireflinkScriptPayloadInterface.js";
11
- import { PromptType } from "../types/promptType.js";
12
- import { INPUTLESS_ACTIONS, ELEMENTLESS_ACTION } from "../constants/supportedActions.js";
13
- import { DomProcessingEngine } from "../../domAnalysis/getRelaventElements.js"
14
- import { getAnnotatedDOM } from "../../utils/DomExtraction/jsForAttributeInjection.js"
15
- import { logger } from "../../utils/logger/logData.js"
16
- export class AutomationRunner implements IAutomationRunner {
17
- constructor(
18
- private readonly request: AutomationRequest,
19
- private pageLoad: number = 20000,
20
- private implicit: number = 15000,
21
- private tokensConsumed: number = 0
22
- ) { }
23
-
24
- async run(): Promise<void> {
25
- const apiService = new FireFlinkApiService();
26
- const scriptRunner = new ScriptRunner(apiService);
27
- const context = new ExecutionContext(this.request);
28
- const configProvider = new ServiceProviderBaseUrlProvider();
29
- const baseUrl = configProvider.getBaseUrl(this.request.serviceProvider);
30
- let domInfo: any = null;
31
- let extractedRelevantDom: any = null;
32
- const llm = new llmAction(this.request.apiKey, baseUrl, this.request.model, this.request.visionApikey);
33
- const stepProcessor = new StepProcessor(llm);
34
-
35
- const stepResult = await stepProcessor.getLLMResponse({ type: PromptType.USER_STORY_TO_LIST, args: {}, input: { userStory: this.request.userStory } });
36
-
37
- const actionHandlers = createActionHandlers(
38
- context,
39
- this.pageLoad,
40
- this.implicit
41
- );
42
- const domProcessor = new DomProcessingEngine();
43
- let stepCount = 0;
44
- const listOfSteps = stepResult.response.manualSteps
45
- if (listOfSteps.length === 0) {
46
- throw new Error("No executable manual steps were returned by the LLM.");
47
- }
48
- for (const step of listOfSteps) {
49
- try {
50
- const start = Math.max(0, stepCount - 3);
51
- const end = Math.min(listOfSteps.length, stepCount + 3);
52
- const priorAndNextSteps = listOfSteps.slice(start, end);
53
-
54
- const result = await stepProcessor.getLLMResponse({
55
- type: PromptType.KEYWORD_EXTRACTOR,
56
- args: { priorAndNextSteps },
57
- input: { currentStep: step }
58
- });
59
-
60
- const action = result.response.action?.toLowerCase();
61
- const handler = actionHandlers[action];
62
-
63
- logger.info
64
- (
65
- `Processing step: "${step}" with action: "${action}" and keywords: ${JSON.stringify(result.response.keywords)}`
66
- );
67
-
68
- if (!handler) {
69
- throw new Error(`Unsupported action: ${action}`);
70
- }
71
-
72
- if (INPUTLESS_ACTIONS.includes(action)) {
73
- await handler(result);
74
- stepCount++;
75
- continue;
76
- }
77
-
78
- if (ELEMENTLESS_ACTION.includes(action)) {
79
- const stepResult = await stepProcessor.getLLMResponse({
80
- type: PromptType.FF_INSPECTOR,
81
- args: {
82
- stepAction: action,
83
- extractedDomJson: "",
84
- priorAndNextSteps,
85
- isAlert: false,
86
- isDrag: false
87
- },
88
- input: { currentStep: step }
89
- });
90
-
91
- await handler({ value: stepResult.response.input_text });
92
- stepCount++;
93
- continue;
94
- }
95
-
96
- const browser = await context.session.getCurrentBrowser();
97
- domInfo = await getAnnotatedDOM(browser);
98
-
99
- extractedRelevantDom = domProcessor.process({
100
- keywords: result.response.keywords,
101
- rawDom: domInfo.dom,
102
- stepIndex: stepCount
103
- });
104
-
105
- const stepResult = await stepProcessor.getLLMResponse({
106
- type: PromptType.FF_INSPECTOR,
107
- args: {
108
- stepAction: action,
109
- extractedDomJson: JSON.stringify(extractedRelevantDom, null, 2),
110
- priorAndNextSteps,
111
- isAlert: false,
112
- isDrag: false
113
- },
114
- input: { currentStep: step }
115
- });
116
-
117
- const fireflinkIndex = stepResult.response.attribute_value;
118
- const xpath = domInfo.selectors[fireflinkIndex];
119
-
120
- if (!xpath) {
121
- throw new Error(`Unable to resolve xpath for ${fireflinkIndex}`);
122
- }
123
-
124
- await handler({
125
- selector: xpath,
126
- value: stepResult.response.input_text,
127
- fireflinkIndex,
128
- pageDOM: domInfo.dom,
129
- elementName: result.response.elementName,
130
- elementType: stepResult.response.elementType
131
- });
132
- domInfo = null;
133
- extractedRelevantDom = null;
134
- stepCount++;
135
- await new Promise(resolve => setImmediate(resolve));
136
- }
137
- catch (error: any) {
138
- context.scriptAppender.add({
139
- nlpName: "CloseBrowser",
140
- elementsData: [],
141
- stepInputs: []
142
- });
143
-
144
- logger.error(`Error executing step "${step}":`, error.message);
145
- break;
146
- }
147
- }
148
- const payload: IPayload = {
149
- scriptName: this.request.scriptName,
150
- scriptType: this.request.scriptType,
151
- projectId: this.request.projectId,
152
- testCaseId: this.request.testCaseId,
153
- promptId: this.request.promptId,
154
- pageDetails: this.request.pageDetails,
155
- generatedBy: this.request.generatedBy,
156
- webSocketId: this.request.webSocketId,
157
- licenseType: this.request.licenseType,
158
- licenseId: this.request.licenseId,
159
- userId: this.request.userId,
160
- topic: this.request.topic,
161
- projectType: this.request.projectType,
162
- tokensConsumed: (await stepProcessor.getResultTokenUsage()).totalTokens,
163
- }
164
- try {
165
- await scriptRunner.runScriptFromPayload(
166
- context.scriptAppender.getData(),
167
- payload,
168
- this.request.token,
169
- this.request.serverHost
170
- );
171
- } catch (error: any) {
172
- throw new Error(
173
- "Failed to send payload to FireFlink API:", { cause: error }
174
- );
175
- }
176
- }
177
- }
1
+ import { IAutomationRunner } from "../interfaces/automationRunnerInterface.js";
2
+ import { AutomationRequest } from "../../core/interfaces/executionDetails.js";
3
+ import { ServiceProviderBaseUrlProvider } from "../../llmConfig/llmConfiguration.js";
4
+ import { llmAction } from "../../ai/llmcalls/llmAction.js";
5
+ import { ExecutionContext } from "./executionContext.js";
6
+ import { createActionHandlers } from "../../core/main/actionHandlerFactory.js";
7
+ import { StepProcessor } from "./stepProcessor.js";
8
+ import { FireFlinkApiService } from "../../service/fireflinkApi.service.js";
9
+ import { ScriptRunner } from "../../service/scriptRunner.service.js";
10
+ import { IPayload } from "../interfaces/fireflinkScriptPayloadInterface.js";
11
+ import { PromptType } from "../types/promptType.js";
12
+ import { INPUTLESS_ACTIONS, ELEMENTLESS_ACTION } from "../constants/supportedActions.js";
13
+ import { DomProcessingEngine } from "../../domAnalysis/getRelaventElements.js"
14
+ import { getAnnotatedDOM } from "../../utils/DomExtraction/jsForAttributeInjection.js"
15
+ import { logger } from "../../utils/logger/logData.js"
16
+ export class AutomationRunner implements IAutomationRunner {
17
+ constructor(
18
+ private readonly request: AutomationRequest,
19
+ private pageLoad: number = 20000,
20
+ private implicit: number = 15000,
21
+ private tokensConsumed: number = 0
22
+ ) { }
23
+
24
+ async run(): Promise<void> {
25
+ const apiService = new FireFlinkApiService();
26
+ const scriptRunner = new ScriptRunner(apiService);
27
+ const context = new ExecutionContext(this.request);
28
+ const configProvider = new ServiceProviderBaseUrlProvider();
29
+ const baseUrl = configProvider.getBaseUrl(this.request.serviceProvider);
30
+ let domInfo: any = null;
31
+ let extractedRelevantDom: any = null;
32
+ const llm = new llmAction(this.request.apiKey, baseUrl, this.request.model, this.request.visionApikey);
33
+ const stepProcessor = new StepProcessor(llm);
34
+
35
+ const stepResult = await stepProcessor.getLLMResponse({ type: PromptType.USER_STORY_TO_LIST, args: {}, input: { userStory: this.request.userStory } });
36
+
37
+ const actionHandlers = createActionHandlers(
38
+ context,
39
+ this.pageLoad,
40
+ this.implicit
41
+ );
42
+ const domProcessor = new DomProcessingEngine();
43
+ let stepCount = 0;
44
+ const listOfSteps = stepResult.response.manualSteps
45
+ if (listOfSteps.length === 0) {
46
+ throw new Error("No executable manual steps were returned by the LLM.");
47
+ }
48
+ for (const step of listOfSteps) {
49
+ try {
50
+ const start = Math.max(0, stepCount - 3);
51
+ const end = Math.min(listOfSteps.length, stepCount + 3);
52
+ const priorAndNextSteps = listOfSteps.slice(start, end);
53
+
54
+ const result = await stepProcessor.getLLMResponse({
55
+ type: PromptType.KEYWORD_EXTRACTOR,
56
+ args: { priorAndNextSteps },
57
+ input: { currentStep: step }
58
+ });
59
+
60
+ const action = result.response.action?.toLowerCase();
61
+ const handler = actionHandlers[action];
62
+
63
+ logger.info
64
+ (
65
+ `Processing step: "${step}" with action: "${action}" and keywords: ${JSON.stringify(result.response.keywords)}`
66
+ );
67
+
68
+ if (!handler) {
69
+ throw new Error(`Unsupported action: ${action}`);
70
+ }
71
+
72
+ if (INPUTLESS_ACTIONS.includes(action)) {
73
+ await handler(result);
74
+ stepCount++;
75
+ continue;
76
+ }
77
+
78
+ if (ELEMENTLESS_ACTION.includes(action)) {
79
+ const stepResult = await stepProcessor.getLLMResponse({
80
+ type: PromptType.FF_INSPECTOR,
81
+ args: {
82
+ stepAction: action,
83
+ extractedDomJson: "",
84
+ priorAndNextSteps,
85
+ isAlert: false,
86
+ isDrag: false
87
+ },
88
+ input: { currentStep: step }
89
+ });
90
+
91
+ await handler({ value: stepResult.response.input_text });
92
+ stepCount++;
93
+ continue;
94
+ }
95
+
96
+ const browser = await context.session.getCurrentBrowser();
97
+ domInfo = await getAnnotatedDOM(browser);
98
+
99
+ extractedRelevantDom = domProcessor.process({
100
+ keywords: result.response.keywords,
101
+ rawDom: domInfo.dom,
102
+ stepIndex: stepCount
103
+ });
104
+
105
+ const stepResult = await stepProcessor.getLLMResponse({
106
+ type: PromptType.FF_INSPECTOR,
107
+ args: {
108
+ stepAction: action,
109
+ extractedDomJson: JSON.stringify(extractedRelevantDom, null, 2),
110
+ priorAndNextSteps,
111
+ isAlert: false,
112
+ isDrag: false
113
+ },
114
+ input: { currentStep: step }
115
+ });
116
+
117
+ const fireflinkIndex = stepResult.response.attribute_value;
118
+ const xpath = domInfo.selectors[fireflinkIndex];
119
+
120
+ if (!xpath) {
121
+ throw new Error(`Unable to resolve xpath for ${fireflinkIndex}`);
122
+ }
123
+
124
+ await handler({
125
+ selector: xpath,
126
+ value: stepResult.response.input_text,
127
+ fireflinkIndex,
128
+ pageDOM: domInfo.dom,
129
+ elementName: result.response.elementName,
130
+ elementType: stepResult.response.elementType
131
+ });
132
+ domInfo = null;
133
+ extractedRelevantDom = null;
134
+ stepCount++;
135
+ await new Promise(resolve => setImmediate(resolve));
136
+ }
137
+ catch (error: any) {
138
+ context.scriptAppender.add({
139
+ nlpName: "CloseBrowser",
140
+ elementsData: [],
141
+ stepInputs: []
142
+ });
143
+
144
+ logger.error(`Error executing step "${step}":`, error.message);
145
+ break;
146
+ }
147
+ }
148
+ const payload: IPayload = {
149
+ scriptName: this.request.scriptName,
150
+ scriptType: this.request.scriptType,
151
+ projectId: this.request.projectId,
152
+ testCaseId: this.request.testCaseId,
153
+ promptId: this.request.promptId,
154
+ pageDetails: this.request.pageDetails,
155
+ generatedBy: this.request.generatedBy,
156
+ webSocketId: this.request.webSocketId,
157
+ licenseType: this.request.licenseType,
158
+ licenseId: this.request.licenseId,
159
+ userId: this.request.userId,
160
+ topic: this.request.topic,
161
+ projectType: this.request.projectType,
162
+ tokensConsumed: (await stepProcessor.getResultTokenUsage()).totalTokens,
163
+ }
164
+ try {
165
+ await scriptRunner.runScriptFromPayload(
166
+ context.scriptAppender.getData(),
167
+ payload,
168
+ this.request.token,
169
+ this.request.serverHost
170
+ );
171
+ } catch (error: any) {
172
+ throw new Error(
173
+ "Failed to send payload to FireFlink API:", { cause: error }
174
+ );
175
+ }
176
+ }
177
+ }
@@ -1,28 +1,28 @@
1
- import { llmAction } from "../../ai/llmcalls/llmAction.js";
2
- import { LLMResultParser } from "../../ai/llmcalls/parseLlmOputput.js";
3
- import { FireFlinkLLMResponse } from "../types/llmResponseType.js"
4
- import { PromptType } from "../types/promptType.js";
5
-
6
- export interface LLMRequest {
7
- type: PromptType;
8
- args: Record<string, any>;
9
- input: Record<string, any>;
10
- }
11
-
12
- export class StepProcessor {
13
- private resultParser: LLMResultParser;
14
-
15
- constructor(private llm: llmAction) {
16
- this.resultParser = new LLMResultParser();
17
- }
18
-
19
- async getResultTokenUsage() {
20
- return this.resultParser.getTokenUsage();
21
- }
22
-
23
-
24
- async getLLMResponse({ type, args, input }: LLMRequest): Promise<{ response: FireFlinkLLMResponse }> {
25
- const response = await this.llm.getLLMResponse(type, args, input);
26
- return this.resultParser.fetchResult(response);
27
- }
28
- }
1
+ import { llmAction } from "../../ai/llmcalls/llmAction.js";
2
+ import { LLMResultParser } from "../../ai/llmcalls/parseLlmOputput.js";
3
+ import { FireFlinkLLMResponse } from "../types/llmResponseType.js"
4
+ import { PromptType } from "../types/promptType.js";
5
+
6
+ export interface LLMRequest {
7
+ type: PromptType;
8
+ args: Record<string, any>;
9
+ input: Record<string, any>;
10
+ }
11
+
12
+ export class StepProcessor {
13
+ private resultParser: LLMResultParser;
14
+
15
+ constructor(private llm: llmAction) {
16
+ this.resultParser = new LLMResultParser();
17
+ }
18
+
19
+ async getResultTokenUsage() {
20
+ return this.resultParser.getTokenUsage();
21
+ }
22
+
23
+
24
+ async getLLMResponse({ type, args, input }: LLMRequest): Promise<{ response: FireFlinkLLMResponse }> {
25
+ const response = await this.llm.getLLMResponse(type, args, input);
26
+ return this.resultParser.fetchResult(response);
27
+ }
28
+ }
@@ -1,11 +1,11 @@
1
- import { IKeywordAction, IDragDropAction, IInputAction, IScrollOrAttributeAction, IManualSteps, IFireFlinkIndex } from "../interfaces/llmResponseInterface.js";
2
-
3
- export type FireFlinkLLMResponse =
4
-
5
- | IKeywordAction
6
- | IInputAction
7
- | IDragDropAction
8
- | IScrollOrAttributeAction
9
- | IManualSteps
10
- | IFireFlinkIndex
1
+ import { IKeywordAction, IDragDropAction, IInputAction, IScrollOrAttributeAction, IManualSteps, IFireFlinkIndex } from "../interfaces/llmResponseInterface.js";
2
+
3
+ export type FireFlinkLLMResponse =
4
+
5
+ | IKeywordAction
6
+ | IInputAction
7
+ | IDragDropAction
8
+ | IScrollOrAttributeAction
9
+ | IManualSteps
10
+ | IFireFlinkIndex
11
11
  | any
@@ -1,4 +1,4 @@
1
- export type visionPromptMessage = (
2
- | { type: "text"; text: string }
3
- | { type: "image_url"; image_url: { url: string } }
4
- )[];
1
+ export type visionPromptMessage = (
2
+ | { type: "text"; text: string }
3
+ | { type: "image_url"; image_url: { url: string } }
4
+ )[];
@@ -1,24 +1,24 @@
1
- import { DomSimplifier } from "./simplifyAndFlatten.js";
2
- import { DomSearcher } from "./searchBest.js";
3
- import { DomRelationshipBuilder } from "./relativeElementsFromDom.js";
4
- export interface DomProcessingRequest {
5
- keywords: string[] | string;
6
- rawDom: string;
7
- stepIndex: number;
8
- }
9
-
10
- export class DomProcessingEngine {
11
-
12
- private simplifier = new DomSimplifier();
13
- private searcher = new DomSearcher();
14
- private relationshipBuilder = new DomRelationshipBuilder();
15
-
16
- public process(request: DomProcessingRequest) {
17
-
18
- const flat = this.simplifier.simplify(request.rawDom);
19
- const searched = this.searcher.search(flat, request.keywords);
20
- const structured = this.relationshipBuilder.build(searched);
21
-
22
- return structured;
23
- }
24
- }
1
+ import { DomSimplifier } from "./simplifyAndFlatten.js";
2
+ import { DomSearcher } from "./searchBest.js";
3
+ import { DomRelationshipBuilder } from "./relativeElementsFromDom.js";
4
+ export interface DomProcessingRequest {
5
+ keywords: string[] | string;
6
+ rawDom: string;
7
+ stepIndex: number;
8
+ }
9
+
10
+ export class DomProcessingEngine {
11
+
12
+ private simplifier = new DomSimplifier();
13
+ private searcher = new DomSearcher();
14
+ private relationshipBuilder = new DomRelationshipBuilder();
15
+
16
+ public process(request: DomProcessingRequest) {
17
+
18
+ const flat = this.simplifier.simplify(request.rawDom);
19
+ const searched = this.searcher.search(flat, request.keywords);
20
+ const structured = this.relationshipBuilder.build(searched);
21
+
22
+ return structured;
23
+ }
24
+ }