linkedin-automation-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +12 -0
- package/.github/workflows/ci.yml +66 -0
- package/.github/workflows/publish.yml +48 -0
- package/.husky/pre-commit +6 -0
- package/.prettierignore +4 -0
- package/.prettierrc +10 -0
- package/AGENTS.md +294 -0
- package/CHANGELOG.md +40 -0
- package/GIT_RELEASE.md +167 -0
- package/LICENSE +21 -0
- package/Makefile +30 -0
- package/NPM_PUBLISHING.md +230 -0
- package/PYEOF +0 -0
- package/README.md +295 -0
- package/TESTING-GUIDE.md +151 -0
- package/cmd/linkedin/main.go +9 -0
- package/dist/agent/action-executor.d.ts +81 -0
- package/dist/agent/action-executor.d.ts.map +1 -0
- package/dist/agent/action-executor.js +170 -0
- package/dist/agent/action-executor.js.map +1 -0
- package/dist/agent/action-executor.test.d.ts +2 -0
- package/dist/agent/action-executor.test.d.ts.map +1 -0
- package/dist/agent/action-executor.test.js +366 -0
- package/dist/agent/action-executor.test.js.map +1 -0
- package/dist/agent/claude-client.d.ts +74 -0
- package/dist/agent/claude-client.d.ts.map +1 -0
- package/dist/agent/claude-client.js +314 -0
- package/dist/agent/claude-client.js.map +1 -0
- package/dist/agent/claude-client.test.d.ts +2 -0
- package/dist/agent/claude-client.test.d.ts.map +1 -0
- package/dist/agent/claude-client.test.js +590 -0
- package/dist/agent/claude-client.test.js.map +1 -0
- package/dist/agent/dom-extractor.d.ts +50 -0
- package/dist/agent/dom-extractor.d.ts.map +1 -0
- package/dist/agent/dom-extractor.js +374 -0
- package/dist/agent/dom-extractor.js.map +1 -0
- package/dist/agent/dom-extractor.test.d.ts +7 -0
- package/dist/agent/dom-extractor.test.d.ts.map +1 -0
- package/dist/agent/dom-extractor.test.js +504 -0
- package/dist/agent/dom-extractor.test.js.map +1 -0
- package/dist/agent/extension-client.d.ts +75 -0
- package/dist/agent/extension-client.d.ts.map +1 -0
- package/dist/agent/extension-client.js +245 -0
- package/dist/agent/extension-client.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +16 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/page-agent.d.ts +76 -0
- package/dist/agent/page-agent.d.ts.map +1 -0
- package/dist/agent/page-agent.js +236 -0
- package/dist/agent/page-agent.js.map +1 -0
- package/dist/agent/types.d.ts +236 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +37 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/agent-commands.d.ts +3 -0
- package/dist/cli/agent-commands.d.ts.map +1 -0
- package/dist/cli/agent-commands.js +250 -0
- package/dist/cli/agent-commands.js.map +1 -0
- package/dist/cli/auth.d.ts +3 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +288 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/company.d.ts +3 -0
- package/dist/cli/company.d.ts.map +1 -0
- package/dist/cli/company.js +55 -0
- package/dist/cli/company.js.map +1 -0
- package/dist/cli/connection.d.ts +3 -0
- package/dist/cli/connection.d.ts.map +1 -0
- package/dist/cli/connection.js +79 -0
- package/dist/cli/connection.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/messages.d.ts +3 -0
- package/dist/cli/messages.d.ts.map +1 -0
- package/dist/cli/messages.js +268 -0
- package/dist/cli/messages.js.map +1 -0
- package/dist/cli/profile.d.ts +3 -0
- package/dist/cli/profile.d.ts.map +1 -0
- package/dist/cli/profile.js +81 -0
- package/dist/cli/profile.js.map +1 -0
- package/dist/cli/profile.test.d.ts +2 -0
- package/dist/cli/profile.test.d.ts.map +1 -0
- package/dist/cli/profile.test.js +15 -0
- package/dist/cli/profile.test.js.map +1 -0
- package/dist/cli/reply.d.ts +3 -0
- package/dist/cli/reply.d.ts.map +1 -0
- package/dist/cli/reply.js +129 -0
- package/dist/cli/reply.js.map +1 -0
- package/dist/core/audit.d.ts +17 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +121 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/audit.test.d.ts +2 -0
- package/dist/core/audit.test.d.ts.map +1 -0
- package/dist/core/audit.test.js +142 -0
- package/dist/core/audit.test.js.map +1 -0
- package/dist/core/browser-cookies.d.ts +19 -0
- package/dist/core/browser-cookies.d.ts.map +1 -0
- package/dist/core/browser-cookies.js +181 -0
- package/dist/core/browser-cookies.js.map +1 -0
- package/dist/core/browser.d.ts +50 -0
- package/dist/core/browser.d.ts.map +1 -0
- package/dist/core/browser.js +318 -0
- package/dist/core/browser.js.map +1 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +103 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/config.test.d.ts +2 -0
- package/dist/core/config.test.d.ts.map +1 -0
- package/dist/core/config.test.js +111 -0
- package/dist/core/config.test.js.map +1 -0
- package/dist/core/storage.d.ts +19 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +124 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/storage.test.d.ts +2 -0
- package/dist/core/storage.test.d.ts.map +1 -0
- package/dist/core/storage.test.js +142 -0
- package/dist/core/storage.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/linkedin/auth.d.ts +22 -0
- package/dist/linkedin/auth.d.ts.map +1 -0
- package/dist/linkedin/auth.js +167 -0
- package/dist/linkedin/auth.js.map +1 -0
- package/dist/linkedin/company-extractor.d.ts +36 -0
- package/dist/linkedin/company-extractor.d.ts.map +1 -0
- package/dist/linkedin/company-extractor.js +211 -0
- package/dist/linkedin/company-extractor.js.map +1 -0
- package/dist/linkedin/company-extractor.test.d.ts +2 -0
- package/dist/linkedin/company-extractor.test.d.ts.map +1 -0
- package/dist/linkedin/company-extractor.test.js +52 -0
- package/dist/linkedin/company-extractor.test.js.map +1 -0
- package/dist/linkedin/connector.d.ts +45 -0
- package/dist/linkedin/connector.d.ts.map +1 -0
- package/dist/linkedin/connector.js +245 -0
- package/dist/linkedin/connector.js.map +1 -0
- package/dist/linkedin/message-sender.d.ts +32 -0
- package/dist/linkedin/message-sender.d.ts.map +1 -0
- package/dist/linkedin/message-sender.js +112 -0
- package/dist/linkedin/message-sender.js.map +1 -0
- package/dist/linkedin/messages.d.ts +78 -0
- package/dist/linkedin/messages.d.ts.map +1 -0
- package/dist/linkedin/messages.js +745 -0
- package/dist/linkedin/messages.js.map +1 -0
- package/dist/linkedin/profile.d.ts +37 -0
- package/dist/linkedin/profile.d.ts.map +1 -0
- package/dist/linkedin/profile.js +268 -0
- package/dist/linkedin/profile.js.map +1 -0
- package/dist/linkedin/profile.test.d.ts +2 -0
- package/dist/linkedin/profile.test.d.ts.map +1 -0
- package/dist/linkedin/profile.test.js +68 -0
- package/dist/linkedin/profile.test.js.map +1 -0
- package/dist/linkedin/reply.d.ts +21 -0
- package/dist/linkedin/reply.d.ts.map +1 -0
- package/dist/linkedin/reply.js +76 -0
- package/dist/linkedin/reply.js.map +1 -0
- package/dist/linkedin/selector-engine.d.ts +69 -0
- package/dist/linkedin/selector-engine.d.ts.map +1 -0
- package/dist/linkedin/selector-engine.js +339 -0
- package/dist/linkedin/selector-engine.js.map +1 -0
- package/dist/linkedin/selector-engine.test.d.ts +2 -0
- package/dist/linkedin/selector-engine.test.d.ts.map +1 -0
- package/dist/linkedin/selector-engine.test.js +135 -0
- package/dist/linkedin/selector-engine.test.js.map +1 -0
- package/dist/linkedin/selectors.d.ts +65 -0
- package/dist/linkedin/selectors.d.ts.map +1 -0
- package/dist/linkedin/selectors.js +261 -0
- package/dist/linkedin/selectors.js.map +1 -0
- package/dist/templates/engine.d.ts +37 -0
- package/dist/templates/engine.d.ts.map +1 -0
- package/dist/templates/engine.js +215 -0
- package/dist/templates/engine.js.map +1 -0
- package/dist/templates/engine.test.d.ts +2 -0
- package/dist/templates/engine.test.d.ts.map +1 -0
- package/dist/templates/engine.test.js +212 -0
- package/dist/templates/engine.test.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +7 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types/index.d.ts +113 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.test.d.ts +2 -0
- package/dist/types/index.test.d.ts.map +1 -0
- package/dist/types/index.test.js +90 -0
- package/dist/types/index.test.js.map +1 -0
- package/dist/utils/paths.d.ts +8 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +68 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +22 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +57 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry.d.ts +18 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +49 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/connection-command.md +52 -0
- package/docs/plans/2025-03-03-linkedin-cli-design.md +280 -0
- package/docs/plans/2025-03-03-linkedin-cli-implementation-plan.md +2087 -0
- package/docs/plans/2025-03-03-linkedin-cli-implementation.md +2420 -0
- package/docs/plans/2026-02-19-linkedin-connection-feature.md +596 -0
- package/docs/plans/2026-02-28-messages-send-feature.md +480 -0
- package/docs/plans/2026-02-28-messages-show-design.md +243 -0
- package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-design.md +394 -0
- package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-plan.md +1592 -0
- package/docs/superpowers/plans/2026-03-13-linkedin-automation-resilience-migration.md +425 -0
- package/docs/superpowers/plans/2026-03-13-playwright-fara-migration.md +1112 -0
- package/docs/superpowers/plans/2026-03-14-page-agent-plan.md +1598 -0
- package/docs/superpowers/plans/2026-03-15-company-profile-extraction.md +591 -0
- package/docs/superpowers/plans/2026-03-15-profile-extraction-plan.md +943 -0
- package/docs/superpowers/specs/2026-03-14-company-profile-extraction-design.md +371 -0
- package/docs/superpowers/specs/2026-03-14-page-agent-design.md +385 -0
- package/docs/superpowers/specs/2026-03-15-profile-extraction-design.md +409 -0
- package/eslint.config.mjs +58 -0
- package/go.mod +9 -0
- package/go.sum +10 -0
- package/import-cookies.js +376 -0
- package/internal/cmd/actions.go +123 -0
- package/internal/cmd/auth.go +108 -0
- package/internal/cmd/connect.go +42 -0
- package/internal/cmd/message.go +44 -0
- package/internal/cmd/people.go +454 -0
- package/internal/cmd/profiles.go +121 -0
- package/internal/cmd/root.go +89 -0
- package/internal/cmd/sequence.go +192 -0
- package/internal/config/config.go +187 -0
- package/internal/config/config_test.go +121 -0
- package/internal/config/profile.go +65 -0
- package/internal/linkedin/navigator.go +195 -0
- package/internal/linkedin/selectors.go +39 -0
- package/internal/linkedin/validator.go +69 -0
- package/internal/pinchtab/client.go +183 -0
- package/internal/pinchtab/client_test.go +67 -0
- package/internal/pinchtab/types.go +50 -0
- package/internal/ratelimit/limiter.go +115 -0
- package/internal/ratelimit/limits.go +32 -0
- package/package.json +67 -0
- package/release.sh +66 -0
- package/scripts/debug-linkedin.js +156 -0
- package/scripts/debug-login.js +193 -0
- package/scripts/extract-from-edge.js +96 -0
- package/scripts/import-cookies.js +101 -0
- package/scripts/poc-show-data.js +205 -0
- package/scripts/proof-of-access.js +87 -0
- package/scripts/prove-connection.js +110 -0
- package/scripts/show-linkedin-data.js +173 -0
- package/src/agent/action-executor.test.ts +464 -0
- package/src/agent/action-executor.ts +203 -0
- package/src/agent/claude-client.test.ts +707 -0
- package/src/agent/claude-client.ts +422 -0
- package/src/agent/dom-extractor.test.ts +574 -0
- package/src/agent/dom-extractor.ts +437 -0
- package/src/agent/extension-client.ts +306 -0
- package/src/agent/index.ts +28 -0
- package/src/agent/page-agent.ts +292 -0
- package/src/agent/types.ts +288 -0
- package/src/cli/agent-commands.ts +274 -0
- package/src/cli/auth.ts +343 -0
- package/src/cli/company.ts +66 -0
- package/src/cli/connection.ts +89 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/messages.ts +338 -0
- package/src/cli/profile.test.ts +14 -0
- package/src/cli/profile.ts +95 -0
- package/src/cli/reply.ts +110 -0
- package/src/core/audit.test.ts +134 -0
- package/src/core/audit.ts +98 -0
- package/src/core/browser-cookies.ts +203 -0
- package/src/core/browser.ts +304 -0
- package/src/core/config.test.ts +90 -0
- package/src/core/config.ts +81 -0
- package/src/core/storage.test.ts +129 -0
- package/src/core/storage.ts +100 -0
- package/src/index.ts +70 -0
- package/src/linkedin/auth.ts +218 -0
- package/src/linkedin/company-extractor.test.ts +58 -0
- package/src/linkedin/company-extractor.ts +222 -0
- package/src/linkedin/connector.ts +336 -0
- package/src/linkedin/message-sender.ts +141 -0
- package/src/linkedin/messages.ts +894 -0
- package/src/linkedin/profile.test.ts +79 -0
- package/src/linkedin/profile.ts +314 -0
- package/src/linkedin/reply.ts +96 -0
- package/src/linkedin/selector-engine.test.ts +167 -0
- package/src/linkedin/selector-engine.ts +393 -0
- package/src/linkedin/selectors.ts +268 -0
- package/src/templates/defaults/followup.txt +14 -0
- package/src/templates/defaults/meeting.txt +16 -0
- package/src/templates/defaults/welcome.txt +14 -0
- package/src/templates/engine.test.ts +228 -0
- package/src/templates/engine.ts +208 -0
- package/src/templates/index.ts +1 -0
- package/src/types/index.test.ts +94 -0
- package/src/types/index.ts +143 -0
- package/src/types/sql.js.d.ts +23 -0
- package/src/utils/paths.ts +33 -0
- package/src/utils/rate-limiter.ts +75 -0
- package/src/utils/retry.ts +78 -0
- package/test-cli.sh +85 -0
- package/test-real-data.sh +97 -0
- package/tsconfig.json +23 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action Executor Module
|
|
3
|
+
*
|
|
4
|
+
* Executes action plans using Playwright browser automation.
|
|
5
|
+
* Maps element IDs from DOM representation to actual click coordinates.
|
|
6
|
+
*/
|
|
7
|
+
import type { Page } from 'playwright';
|
|
8
|
+
import type { Action, DOMRepresentation, ExecutedAction } from './types';
|
|
9
|
+
/**
|
|
10
|
+
* Result of executing a single action
|
|
11
|
+
*/
|
|
12
|
+
export interface ExecutionResult {
|
|
13
|
+
/** Whether the action executed successfully */
|
|
14
|
+
success: boolean;
|
|
15
|
+
/** Error message if the action failed */
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Executes actions on a Playwright page using DOM representation
|
|
20
|
+
*/
|
|
21
|
+
export declare class ActionExecutor {
|
|
22
|
+
private readonly page;
|
|
23
|
+
private readonly dom;
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new ActionExecutor
|
|
26
|
+
* @param page - Playwright page instance
|
|
27
|
+
* @param dom - DOM representation with element bounding boxes
|
|
28
|
+
*/
|
|
29
|
+
constructor(page: Page, dom: DOMRepresentation);
|
|
30
|
+
/**
|
|
31
|
+
* Execute a single action
|
|
32
|
+
* @param action - The action to execute
|
|
33
|
+
* @returns Promise<ExecutionResult> with success status
|
|
34
|
+
*/
|
|
35
|
+
execute(action: Action): Promise<ExecutionResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Execute a list of actions sequentially
|
|
38
|
+
* @param actions - Array of actions to execute
|
|
39
|
+
* @returns Promise<ExecutedAction[]> with execution results and timestamps
|
|
40
|
+
*/
|
|
41
|
+
executeAll(actions: Action[]): Promise<ExecutedAction[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Find element by ID in the DOM representation
|
|
44
|
+
* @param elementId - The element ID to find
|
|
45
|
+
* @returns DOMElement or undefined if not found
|
|
46
|
+
*/
|
|
47
|
+
private findElement;
|
|
48
|
+
/**
|
|
49
|
+
* Execute a click action
|
|
50
|
+
* @param elementId - ID of the element to click
|
|
51
|
+
* @returns ExecutionResult
|
|
52
|
+
*/
|
|
53
|
+
private executeClick;
|
|
54
|
+
/**
|
|
55
|
+
* Execute a type action
|
|
56
|
+
* @param elementId - ID of the input element
|
|
57
|
+
* @param text - Text to type
|
|
58
|
+
* @returns ExecutionResult
|
|
59
|
+
*/
|
|
60
|
+
private executeType;
|
|
61
|
+
/**
|
|
62
|
+
* Execute a wait action
|
|
63
|
+
* @param durationMs - Duration to wait in milliseconds
|
|
64
|
+
* @returns ExecutionResult
|
|
65
|
+
*/
|
|
66
|
+
private executeWait;
|
|
67
|
+
/**
|
|
68
|
+
* Execute a navigate action
|
|
69
|
+
* @param url - URL to navigate to
|
|
70
|
+
* @returns ExecutionResult
|
|
71
|
+
*/
|
|
72
|
+
private executeNavigate;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Create a new ActionExecutor instance
|
|
76
|
+
* @param page - Playwright page instance
|
|
77
|
+
* @param dom - DOM representation with element bounding boxes
|
|
78
|
+
* @returns ActionExecutor instance
|
|
79
|
+
*/
|
|
80
|
+
export declare function createActionExecutor(page: Page, dom: DOMRepresentation): ActionExecutor;
|
|
81
|
+
//# sourceMappingURL=action-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-executor.d.ts","sourceRoot":"","sources":["../../src/agent/action-executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAc,cAAc,EAAE,MAAM,SAAS,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IAExC;;;;OAIG;gBACS,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,iBAAiB;IAK9C;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAyBvD;;;;OAIG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAiC9D;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAInB;;;;OAIG;YACW,YAAY;IAoB1B;;;;;OAKG;YACW,WAAW;IAyBzB;;;;OAIG;YACW,WAAW;IAKzB;;;;OAIG;YACW,eAAe;CAO9B;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,iBAAiB,GAAG,cAAc,CAEvF"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Action Executor Module
|
|
4
|
+
*
|
|
5
|
+
* Executes action plans using Playwright browser automation.
|
|
6
|
+
* Maps element IDs from DOM representation to actual click coordinates.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ActionExecutor = void 0;
|
|
10
|
+
exports.createActionExecutor = createActionExecutor;
|
|
11
|
+
/**
|
|
12
|
+
* Executes actions on a Playwright page using DOM representation
|
|
13
|
+
*/
|
|
14
|
+
class ActionExecutor {
|
|
15
|
+
page;
|
|
16
|
+
dom;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new ActionExecutor
|
|
19
|
+
* @param page - Playwright page instance
|
|
20
|
+
* @param dom - DOM representation with element bounding boxes
|
|
21
|
+
*/
|
|
22
|
+
constructor(page, dom) {
|
|
23
|
+
this.page = page;
|
|
24
|
+
this.dom = dom;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Execute a single action
|
|
28
|
+
* @param action - The action to execute
|
|
29
|
+
* @returns Promise<ExecutionResult> with success status
|
|
30
|
+
*/
|
|
31
|
+
async execute(action) {
|
|
32
|
+
try {
|
|
33
|
+
switch (action.type) {
|
|
34
|
+
case 'click':
|
|
35
|
+
return await this.executeClick(action.elementId);
|
|
36
|
+
case 'type':
|
|
37
|
+
return await this.executeType(action.elementId, action.text);
|
|
38
|
+
case 'wait':
|
|
39
|
+
return await this.executeWait(action.durationMs);
|
|
40
|
+
case 'navigate':
|
|
41
|
+
return await this.executeNavigate(action.url);
|
|
42
|
+
default:
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: `Unknown action type: ${action.type}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: error instanceof Error ? error.message : String(error),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Execute a list of actions sequentially
|
|
58
|
+
* @param actions - Array of actions to execute
|
|
59
|
+
* @returns Promise<ExecutedAction[]> with execution results and timestamps
|
|
60
|
+
*/
|
|
61
|
+
async executeAll(actions) {
|
|
62
|
+
const executedActions = [];
|
|
63
|
+
for (const action of actions) {
|
|
64
|
+
// Log the action before execution
|
|
65
|
+
console.log(`Executing action: ${action.description}`);
|
|
66
|
+
const result = await this.execute(action);
|
|
67
|
+
const executedAction = {
|
|
68
|
+
action,
|
|
69
|
+
success: result.success,
|
|
70
|
+
timestamp: new Date(),
|
|
71
|
+
error: result.error,
|
|
72
|
+
};
|
|
73
|
+
executedActions.push(executedAction);
|
|
74
|
+
// Stop on first failure
|
|
75
|
+
if (!result.success) {
|
|
76
|
+
console.error(`Action failed: ${result.error}`);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
// Add 500ms delay between actions (except after the last one)
|
|
80
|
+
if (actions.indexOf(action) < actions.length - 1) {
|
|
81
|
+
await this.page.waitForTimeout(500);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return executedActions;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Find element by ID in the DOM representation
|
|
88
|
+
* @param elementId - The element ID to find
|
|
89
|
+
* @returns DOMElement or undefined if not found
|
|
90
|
+
*/
|
|
91
|
+
findElement(elementId) {
|
|
92
|
+
return this.dom.elements.find((el) => el.id === elementId);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Execute a click action
|
|
96
|
+
* @param elementId - ID of the element to click
|
|
97
|
+
* @returns ExecutionResult
|
|
98
|
+
*/
|
|
99
|
+
async executeClick(elementId) {
|
|
100
|
+
const element = this.findElement(elementId);
|
|
101
|
+
if (!element) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: `Element not found: ${elementId}`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Calculate center of bounding box
|
|
108
|
+
const centerX = element.bbox.x + element.bbox.width / 2;
|
|
109
|
+
const centerY = element.bbox.y + element.bbox.height / 2;
|
|
110
|
+
// Perform the click at the calculated coordinates
|
|
111
|
+
await this.page.mouse.click(centerX, centerY);
|
|
112
|
+
return { success: true };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Execute a type action
|
|
116
|
+
* @param elementId - ID of the input element
|
|
117
|
+
* @param text - Text to type
|
|
118
|
+
* @returns ExecutionResult
|
|
119
|
+
*/
|
|
120
|
+
async executeType(elementId, text) {
|
|
121
|
+
const element = this.findElement(elementId);
|
|
122
|
+
if (!element) {
|
|
123
|
+
return {
|
|
124
|
+
success: false,
|
|
125
|
+
error: `Element not found: ${elementId}`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// Click element first to focus
|
|
129
|
+
const centerX = element.bbox.x + element.bbox.width / 2;
|
|
130
|
+
const centerY = element.bbox.y + element.bbox.height / 2;
|
|
131
|
+
await this.page.mouse.click(centerX, centerY);
|
|
132
|
+
// Wait 100ms for focus to settle
|
|
133
|
+
await this.page.waitForTimeout(100);
|
|
134
|
+
// Type the text
|
|
135
|
+
await this.page.keyboard.type(text);
|
|
136
|
+
return { success: true };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Execute a wait action
|
|
140
|
+
* @param durationMs - Duration to wait in milliseconds
|
|
141
|
+
* @returns ExecutionResult
|
|
142
|
+
*/
|
|
143
|
+
async executeWait(durationMs) {
|
|
144
|
+
await this.page.waitForTimeout(durationMs);
|
|
145
|
+
return { success: true };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Execute a navigate action
|
|
149
|
+
* @param url - URL to navigate to
|
|
150
|
+
* @returns ExecutionResult
|
|
151
|
+
*/
|
|
152
|
+
async executeNavigate(url) {
|
|
153
|
+
await this.page.goto(url, {
|
|
154
|
+
waitUntil: 'domcontentloaded',
|
|
155
|
+
timeout: 30000,
|
|
156
|
+
});
|
|
157
|
+
return { success: true };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.ActionExecutor = ActionExecutor;
|
|
161
|
+
/**
|
|
162
|
+
* Create a new ActionExecutor instance
|
|
163
|
+
* @param page - Playwright page instance
|
|
164
|
+
* @param dom - DOM representation with element bounding boxes
|
|
165
|
+
* @returns ActionExecutor instance
|
|
166
|
+
*/
|
|
167
|
+
function createActionExecutor(page, dom) {
|
|
168
|
+
return new ActionExecutor(page, dom);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=action-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-executor.js","sourceRoot":"","sources":["../../src/agent/action-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-executor.test.d.ts","sourceRoot":"","sources":["../../src/agent/action-executor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const action_executor_1 = require("./action-executor");
|
|
5
|
+
(0, vitest_1.describe)('ActionExecutor', () => {
|
|
6
|
+
let mockPage;
|
|
7
|
+
let mockDOM;
|
|
8
|
+
let executor;
|
|
9
|
+
(0, vitest_1.beforeEach)(() => {
|
|
10
|
+
// Create mock Page
|
|
11
|
+
mockPage = {
|
|
12
|
+
url: vitest_1.vi.fn().mockReturnValue('https://linkedin.com/in/test-user'),
|
|
13
|
+
title: vitest_1.vi.fn().mockResolvedValue('Test User - LinkedIn'),
|
|
14
|
+
mouse: {
|
|
15
|
+
click: vitest_1.vi.fn().mockResolvedValue(undefined),
|
|
16
|
+
},
|
|
17
|
+
keyboard: {
|
|
18
|
+
type: vitest_1.vi.fn().mockResolvedValue(undefined),
|
|
19
|
+
},
|
|
20
|
+
waitForTimeout: vitest_1.vi.fn().mockResolvedValue(undefined),
|
|
21
|
+
goto: vitest_1.vi.fn().mockResolvedValue(undefined),
|
|
22
|
+
};
|
|
23
|
+
// Create mock DOM representation
|
|
24
|
+
mockDOM = {
|
|
25
|
+
url: 'https://linkedin.com/in/test-user',
|
|
26
|
+
title: 'Test User - LinkedIn',
|
|
27
|
+
elements: [
|
|
28
|
+
{
|
|
29
|
+
id: 'btn-connect',
|
|
30
|
+
tag: 'button',
|
|
31
|
+
role: 'button',
|
|
32
|
+
ariaLabel: 'Connect',
|
|
33
|
+
text: 'Connect',
|
|
34
|
+
visible: true,
|
|
35
|
+
enabled: true,
|
|
36
|
+
bbox: { x: 100, y: 200, width: 120, height: 40 },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'input-message',
|
|
40
|
+
tag: 'textarea',
|
|
41
|
+
role: 'textbox',
|
|
42
|
+
text: '',
|
|
43
|
+
visible: true,
|
|
44
|
+
enabled: true,
|
|
45
|
+
bbox: { x: 50, y: 300, width: 400, height: 100 },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'btn-submit',
|
|
49
|
+
tag: 'button',
|
|
50
|
+
text: 'Submit',
|
|
51
|
+
visible: true,
|
|
52
|
+
enabled: true,
|
|
53
|
+
bbox: { x: 200, y: 420, width: 80, height: 35 },
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
metadata: {
|
|
57
|
+
pageType: 'profile',
|
|
58
|
+
profileName: 'Test User',
|
|
59
|
+
connectionState: 'not_connected',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
executor = new action_executor_1.ActionExecutor(mockPage, mockDOM);
|
|
63
|
+
});
|
|
64
|
+
(0, vitest_1.describe)('constructor', () => {
|
|
65
|
+
(0, vitest_1.it)('should create an ActionExecutor with page and DOM', () => {
|
|
66
|
+
const executor = new action_executor_1.ActionExecutor(mockPage, mockDOM);
|
|
67
|
+
(0, vitest_1.expect)(executor).toBeInstanceOf(action_executor_1.ActionExecutor);
|
|
68
|
+
});
|
|
69
|
+
(0, vitest_1.it)('should create via factory function', () => {
|
|
70
|
+
const executor = (0, action_executor_1.createActionExecutor)(mockPage, mockDOM);
|
|
71
|
+
(0, vitest_1.expect)(executor).toBeInstanceOf(action_executor_1.ActionExecutor);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
(0, vitest_1.describe)('execute - click action', () => {
|
|
75
|
+
(0, vitest_1.it)('should execute click action with correct coordinates', async () => {
|
|
76
|
+
const action = {
|
|
77
|
+
type: 'click',
|
|
78
|
+
elementId: 'btn-connect',
|
|
79
|
+
description: 'Click connect button',
|
|
80
|
+
};
|
|
81
|
+
const result = await executor.execute(action);
|
|
82
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
83
|
+
(0, vitest_1.expect)(result.error).toBeUndefined();
|
|
84
|
+
// Verify click was called with center coordinates
|
|
85
|
+
// centerX = 100 + 120/2 = 160, centerY = 200 + 40/2 = 220
|
|
86
|
+
(0, vitest_1.expect)(mockPage.mouse.click).toHaveBeenCalledWith(160, 220);
|
|
87
|
+
});
|
|
88
|
+
(0, vitest_1.it)('should handle click on element with different bbox', async () => {
|
|
89
|
+
const action = {
|
|
90
|
+
type: 'click',
|
|
91
|
+
elementId: 'btn-submit',
|
|
92
|
+
description: 'Click submit button',
|
|
93
|
+
};
|
|
94
|
+
const result = await executor.execute(action);
|
|
95
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
96
|
+
// centerX = 200 + 80/2 = 240, centerY = 420 + 35/2 = 437.5
|
|
97
|
+
(0, vitest_1.expect)(mockPage.mouse.click).toHaveBeenCalledWith(240, 437.5);
|
|
98
|
+
});
|
|
99
|
+
(0, vitest_1.it)('should return error when element not found', async () => {
|
|
100
|
+
const action = {
|
|
101
|
+
type: 'click',
|
|
102
|
+
elementId: 'non-existent-id',
|
|
103
|
+
description: 'Click non-existent button',
|
|
104
|
+
};
|
|
105
|
+
const result = await executor.execute(action);
|
|
106
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
107
|
+
(0, vitest_1.expect)(result.error).toContain('Element not found');
|
|
108
|
+
(0, vitest_1.expect)(result.error).toContain('non-existent-id');
|
|
109
|
+
(0, vitest_1.expect)(mockPage.mouse.click).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
(0, vitest_1.describe)('execute - type action', () => {
|
|
113
|
+
(0, vitest_1.it)('should execute type action by clicking then typing', async () => {
|
|
114
|
+
const action = {
|
|
115
|
+
type: 'type',
|
|
116
|
+
elementId: 'input-message',
|
|
117
|
+
text: 'Hello, this is a test message',
|
|
118
|
+
description: 'Type message in textarea',
|
|
119
|
+
};
|
|
120
|
+
const result = await executor.execute(action);
|
|
121
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
122
|
+
// Should click to focus first
|
|
123
|
+
// centerX = 50 + 400/2 = 250, centerY = 300 + 100/2 = 350
|
|
124
|
+
(0, vitest_1.expect)(mockPage.mouse.click).toHaveBeenCalledWith(250, 350);
|
|
125
|
+
// Should wait 100ms for focus
|
|
126
|
+
(0, vitest_1.expect)(mockPage.waitForTimeout).toHaveBeenCalledWith(100);
|
|
127
|
+
// Should type the text
|
|
128
|
+
(0, vitest_1.expect)(mockPage.keyboard.type).toHaveBeenCalledWith('Hello, this is a test message');
|
|
129
|
+
});
|
|
130
|
+
(0, vitest_1.it)('should handle empty text', async () => {
|
|
131
|
+
const action = {
|
|
132
|
+
type: 'type',
|
|
133
|
+
elementId: 'input-message',
|
|
134
|
+
text: '',
|
|
135
|
+
description: 'Type empty string',
|
|
136
|
+
};
|
|
137
|
+
const result = await executor.execute(action);
|
|
138
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
139
|
+
(0, vitest_1.expect)(mockPage.keyboard.type).toHaveBeenCalledWith('');
|
|
140
|
+
});
|
|
141
|
+
(0, vitest_1.it)('should return error when element not found for type action', async () => {
|
|
142
|
+
const action = {
|
|
143
|
+
type: 'type',
|
|
144
|
+
elementId: 'non-existent-input',
|
|
145
|
+
text: 'Some text',
|
|
146
|
+
description: 'Type in non-existent field',
|
|
147
|
+
};
|
|
148
|
+
const result = await executor.execute(action);
|
|
149
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
150
|
+
(0, vitest_1.expect)(result.error).toContain('Element not found');
|
|
151
|
+
(0, vitest_1.expect)(mockPage.mouse.click).not.toHaveBeenCalled();
|
|
152
|
+
(0, vitest_1.expect)(mockPage.keyboard.type).not.toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
(0, vitest_1.describe)('execute - wait action', () => {
|
|
156
|
+
(0, vitest_1.it)('should execute wait action with specified duration', async () => {
|
|
157
|
+
const action = {
|
|
158
|
+
type: 'wait',
|
|
159
|
+
durationMs: 2000,
|
|
160
|
+
description: 'Wait for modal to appear',
|
|
161
|
+
};
|
|
162
|
+
const result = await executor.execute(action);
|
|
163
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
164
|
+
(0, vitest_1.expect)(mockPage.waitForTimeout).toHaveBeenCalledWith(2000);
|
|
165
|
+
});
|
|
166
|
+
(0, vitest_1.it)('should handle zero duration wait', async () => {
|
|
167
|
+
const action = {
|
|
168
|
+
type: 'wait',
|
|
169
|
+
durationMs: 0,
|
|
170
|
+
description: 'Wait 0ms',
|
|
171
|
+
};
|
|
172
|
+
const result = await executor.execute(action);
|
|
173
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
174
|
+
(0, vitest_1.expect)(mockPage.waitForTimeout).toHaveBeenCalledWith(0);
|
|
175
|
+
});
|
|
176
|
+
(0, vitest_1.it)('should handle short duration wait', async () => {
|
|
177
|
+
const action = {
|
|
178
|
+
type: 'wait',
|
|
179
|
+
durationMs: 100,
|
|
180
|
+
description: 'Wait 100ms',
|
|
181
|
+
};
|
|
182
|
+
const result = await executor.execute(action);
|
|
183
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
184
|
+
(0, vitest_1.expect)(mockPage.waitForTimeout).toHaveBeenCalledWith(100);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
(0, vitest_1.describe)('execute - navigate action', () => {
|
|
188
|
+
(0, vitest_1.it)('should execute navigate action with correct options', async () => {
|
|
189
|
+
const action = {
|
|
190
|
+
type: 'navigate',
|
|
191
|
+
url: 'https://linkedin.com/in/another-user',
|
|
192
|
+
description: 'Navigate to another profile',
|
|
193
|
+
};
|
|
194
|
+
const result = await executor.execute(action);
|
|
195
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
196
|
+
(0, vitest_1.expect)(mockPage.goto).toHaveBeenCalledWith('https://linkedin.com/in/another-user', {
|
|
197
|
+
waitUntil: 'domcontentloaded',
|
|
198
|
+
timeout: 30000,
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
(0, vitest_1.it)('should handle navigation to root URL', async () => {
|
|
202
|
+
const action = {
|
|
203
|
+
type: 'navigate',
|
|
204
|
+
url: 'https://linkedin.com',
|
|
205
|
+
description: 'Navigate to LinkedIn home',
|
|
206
|
+
};
|
|
207
|
+
const result = await executor.execute(action);
|
|
208
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
209
|
+
(0, vitest_1.expect)(mockPage.goto).toHaveBeenCalledWith('https://linkedin.com', {
|
|
210
|
+
waitUntil: 'domcontentloaded',
|
|
211
|
+
timeout: 30000,
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
(0, vitest_1.describe)('execute - unknown action type', () => {
|
|
216
|
+
(0, vitest_1.it)('should return error for unknown action type', async () => {
|
|
217
|
+
const action = {
|
|
218
|
+
type: 'unknown',
|
|
219
|
+
description: 'Unknown action',
|
|
220
|
+
};
|
|
221
|
+
const result = await executor.execute(action);
|
|
222
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
223
|
+
(0, vitest_1.expect)(result.error).toContain('Unknown action type');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
(0, vitest_1.describe)('execute - error handling', () => {
|
|
227
|
+
(0, vitest_1.it)('should handle mouse click errors', async () => {
|
|
228
|
+
const errorMessage = 'Element not interactable';
|
|
229
|
+
vitest_1.vi.mocked(mockPage.mouse.click).mockRejectedValueOnce(new Error(errorMessage));
|
|
230
|
+
const action = {
|
|
231
|
+
type: 'click',
|
|
232
|
+
elementId: 'btn-connect',
|
|
233
|
+
description: 'Click button',
|
|
234
|
+
};
|
|
235
|
+
const result = await executor.execute(action);
|
|
236
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
237
|
+
(0, vitest_1.expect)(result.error).toBe(errorMessage);
|
|
238
|
+
});
|
|
239
|
+
(0, vitest_1.it)('should handle keyboard type errors', async () => {
|
|
240
|
+
const errorMessage = 'Element is not focused';
|
|
241
|
+
vitest_1.vi.mocked(mockPage.keyboard.type).mockRejectedValueOnce(new Error(errorMessage));
|
|
242
|
+
const action = {
|
|
243
|
+
type: 'type',
|
|
244
|
+
elementId: 'input-message',
|
|
245
|
+
text: 'Test',
|
|
246
|
+
description: 'Type text',
|
|
247
|
+
};
|
|
248
|
+
const result = await executor.execute(action);
|
|
249
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
250
|
+
(0, vitest_1.expect)(result.error).toBe(errorMessage);
|
|
251
|
+
});
|
|
252
|
+
(0, vitest_1.it)('should handle navigation errors', async () => {
|
|
253
|
+
const errorMessage = 'net::ERR_NAME_NOT_RESOLVED';
|
|
254
|
+
vitest_1.vi.mocked(mockPage.goto).mockRejectedValueOnce(new Error(errorMessage));
|
|
255
|
+
const action = {
|
|
256
|
+
type: 'navigate',
|
|
257
|
+
url: 'https://invalid-url',
|
|
258
|
+
description: 'Navigate to invalid URL',
|
|
259
|
+
};
|
|
260
|
+
const result = await executor.execute(action);
|
|
261
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
262
|
+
(0, vitest_1.expect)(result.error).toBe(errorMessage);
|
|
263
|
+
});
|
|
264
|
+
(0, vitest_1.it)('should handle non-Error exceptions', async () => {
|
|
265
|
+
vitest_1.vi.mocked(mockPage.mouse.click).mockRejectedValueOnce('String error');
|
|
266
|
+
const action = {
|
|
267
|
+
type: 'click',
|
|
268
|
+
elementId: 'btn-connect',
|
|
269
|
+
description: 'Click button',
|
|
270
|
+
};
|
|
271
|
+
const result = await executor.execute(action);
|
|
272
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
273
|
+
(0, vitest_1.expect)(result.error).toBe('String error');
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
(0, vitest_1.describe)('executeAll', () => {
|
|
277
|
+
(0, vitest_1.it)('should execute multiple actions sequentially', async () => {
|
|
278
|
+
const actions = [
|
|
279
|
+
{ type: 'click', elementId: 'btn-connect', description: 'Click connect' },
|
|
280
|
+
{ type: 'wait', durationMs: 1000, description: 'Wait for modal' },
|
|
281
|
+
{ type: 'click', elementId: 'btn-submit', description: 'Click submit' },
|
|
282
|
+
];
|
|
283
|
+
const results = await executor.executeAll(actions);
|
|
284
|
+
(0, vitest_1.expect)(results).toHaveLength(3);
|
|
285
|
+
(0, vitest_1.expect)(results[0].success).toBe(true);
|
|
286
|
+
(0, vitest_1.expect)(results[0].action.type).toBe('click');
|
|
287
|
+
(0, vitest_1.expect)(results[1].success).toBe(true);
|
|
288
|
+
(0, vitest_1.expect)(results[1].action.type).toBe('wait');
|
|
289
|
+
(0, vitest_1.expect)(results[2].success).toBe(true);
|
|
290
|
+
(0, vitest_1.expect)(results[2].action.type).toBe('click');
|
|
291
|
+
// Each result should have a timestamp
|
|
292
|
+
results.forEach((result) => {
|
|
293
|
+
(0, vitest_1.expect)(result.timestamp).toBeInstanceOf(Date);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
(0, vitest_1.it)('should add 500ms delay between actions', async () => {
|
|
297
|
+
const actions = [
|
|
298
|
+
{ type: 'click', elementId: 'btn-connect', description: 'Click connect' },
|
|
299
|
+
{ type: 'click', elementId: 'btn-submit', description: 'Click submit' },
|
|
300
|
+
];
|
|
301
|
+
await executor.executeAll(actions);
|
|
302
|
+
// Should have waitForTimeout called for the 500ms delay between actions
|
|
303
|
+
// plus potentially 100ms for type actions (not in this test)
|
|
304
|
+
const waitCalls = vitest_1.vi.mocked(mockPage.waitForTimeout).mock.calls;
|
|
305
|
+
const hasDelay = waitCalls.some((call) => call[0] === 500);
|
|
306
|
+
(0, vitest_1.expect)(hasDelay).toBe(true);
|
|
307
|
+
});
|
|
308
|
+
(0, vitest_1.it)('should stop on first failure', async () => {
|
|
309
|
+
const actions = [
|
|
310
|
+
{ type: 'click', elementId: 'btn-connect', description: 'Click connect' },
|
|
311
|
+
{ type: 'click', elementId: 'non-existent', description: 'Click non-existent' },
|
|
312
|
+
{ type: 'click', elementId: 'btn-submit', description: 'Should not execute' },
|
|
313
|
+
];
|
|
314
|
+
const results = await executor.executeAll(actions);
|
|
315
|
+
// Should only have 2 results (stopped after failure)
|
|
316
|
+
(0, vitest_1.expect)(results).toHaveLength(2);
|
|
317
|
+
(0, vitest_1.expect)(results[0].success).toBe(true);
|
|
318
|
+
(0, vitest_1.expect)(results[1].success).toBe(false);
|
|
319
|
+
(0, vitest_1.expect)(results[1].error).toContain('Element not found');
|
|
320
|
+
// Third action should not have been executed
|
|
321
|
+
(0, vitest_1.expect)(mockPage.mouse.click).toHaveBeenCalledTimes(1);
|
|
322
|
+
});
|
|
323
|
+
(0, vitest_1.it)('should log each action before execution', async () => {
|
|
324
|
+
const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
325
|
+
const actions = [
|
|
326
|
+
{ type: 'click', elementId: 'btn-connect', description: 'Click connect button' },
|
|
327
|
+
];
|
|
328
|
+
await executor.executeAll(actions);
|
|
329
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith('Executing action: Click connect button');
|
|
330
|
+
consoleSpy.mockRestore();
|
|
331
|
+
});
|
|
332
|
+
(0, vitest_1.it)('should log error when action fails', async () => {
|
|
333
|
+
const consoleErrorSpy = vitest_1.vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
334
|
+
const actions = [
|
|
335
|
+
{ type: 'click', elementId: 'non-existent', description: 'Click non-existent' },
|
|
336
|
+
];
|
|
337
|
+
await executor.executeAll(actions);
|
|
338
|
+
(0, vitest_1.expect)(consoleErrorSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Action failed:'));
|
|
339
|
+
consoleErrorSpy.mockRestore();
|
|
340
|
+
});
|
|
341
|
+
(0, vitest_1.it)('should handle empty action array', async () => {
|
|
342
|
+
const results = await executor.executeAll([]);
|
|
343
|
+
(0, vitest_1.expect)(results).toHaveLength(0);
|
|
344
|
+
});
|
|
345
|
+
(0, vitest_1.it)('should handle single action', async () => {
|
|
346
|
+
const actions = [{ type: 'wait', durationMs: 500, description: 'Wait briefly' }];
|
|
347
|
+
const results = await executor.executeAll(actions);
|
|
348
|
+
(0, vitest_1.expect)(results).toHaveLength(1);
|
|
349
|
+
(0, vitest_1.expect)(results[0].success).toBe(true);
|
|
350
|
+
// No delay after the last action
|
|
351
|
+
const waitCalls = vitest_1.vi.mocked(mockPage.waitForTimeout).mock.calls;
|
|
352
|
+
(0, vitest_1.expect)(waitCalls).toHaveLength(1); // Only the wait action itself, no delay after
|
|
353
|
+
});
|
|
354
|
+
(0, vitest_1.it)('should include error in ExecutedAction when action fails', async () => {
|
|
355
|
+
const actions = [
|
|
356
|
+
{ type: 'click', elementId: 'non-existent', description: 'Click non-existent' },
|
|
357
|
+
];
|
|
358
|
+
const results = await executor.executeAll(actions);
|
|
359
|
+
(0, vitest_1.expect)(results[0].success).toBe(false);
|
|
360
|
+
(0, vitest_1.expect)(results[0].error).toBeDefined();
|
|
361
|
+
(0, vitest_1.expect)(results[0].error).toContain('Element not found');
|
|
362
|
+
(0, vitest_1.expect)(results[0].timestamp).toBeInstanceOf(Date);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
//# sourceMappingURL=action-executor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-executor.test.js","sourceRoot":"","sources":["../../src/agent/action-executor.test.ts"],"names":[],"mappings":";;AAAA,mCAA8D;AAC9D,uDAAyE;AAWzE,IAAA,iBAAQ,EAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,IAAI,QAAc,CAAC;IACnB,IAAI,OAA0B,CAAC;IAC/B,IAAI,QAAwB,CAAC;IAE7B,IAAA,mBAAU,EAAC,GAAG,EAAE;QACd,mBAAmB;QACnB,QAAQ,GAAG;YACT,GAAG,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,mCAAmC,CAAC;YACjE,KAAK,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,sBAAsB,CAAC;YACxD,KAAK,EAAE;gBACL,KAAK,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;aAC5C;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;aAC3C;YACD,cAAc,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;YACpD,IAAI,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;SACxB,CAAC;QAErB,iCAAiC;QACjC,OAAO,GAAG;YACR,GAAG,EAAE,mCAAmC;YACxC,KAAK,EAAE,sBAAsB;YAC7B,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,aAAa;oBACjB,GAAG,EAAE,QAAQ;oBACb,IAAI,EAAE,QAAQ;oBACd,SAAS,EAAE,SAAS;oBACpB,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;iBACjD;gBACD;oBACE,EAAE,EAAE,eAAe;oBACnB,GAAG,EAAE,UAAU;oBACf,IAAI,EAAE,SAAS;oBACf,IAAI,EAAE,EAAE;oBACR,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;iBACjD;gBACD;oBACE,EAAE,EAAE,YAAY;oBAChB,GAAG,EAAE,QAAQ;oBACb,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;iBAChD;aACF;YACD,QAAQ,EAAE;gBACR,QAAQ,EAAE,SAAS;gBACnB,WAAW,EAAE,WAAW;gBACxB,eAAe,EAAE,eAAe;aACjC;SACF,CAAC;QAEF,QAAQ,GAAG,IAAI,gCAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,IAAA,WAAE,EAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,QAAQ,GAAG,IAAI,gCAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACvD,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,gCAAc,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,QAAQ,GAAG,IAAA,sCAAoB,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACzD,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,gCAAc,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,IAAA,WAAE,EAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;YACpE,MAAM,MAAM,GAAgB;gBAC1B,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,aAAa;gBACxB,WAAW,EAAE,sBAAsB;aACpC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;YAErC,kDAAkD;YAClD,0DAA0D;YAC1D,IAAA,eAAM,EAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,MAAM,GAAgB;gBAC1B,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,YAAY;gBACvB,WAAW,EAAE,qBAAqB;aACnC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,2DAA2D;YAC3D,IAAA,eAAM,EAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,MAAM,GAAgB;gBAC1B,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,iBAAiB;gBAC5B,WAAW,EAAE,2BAA2B;aACzC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;YACpD,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;YAClD,IAAA,eAAM,EAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,IAAA,WAAE,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,MAAM,GAAe;gBACzB,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,eAAe;gBAC1B,IAAI,EAAE,+BAA+B;gBACrC,WAAW,EAAE,0BAA0B;aACxC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAElC,8BAA8B;YAC9B,0DAA0D;YAC1D,IAAA,eAAM,EAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAE5D,8BAA8B;YAC9B,IAAA,eAAM,EAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YAE1D,uBAAuB;YACvB,IAAA,eAAM,EAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,+BAA+B,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,MAAM,MAAM,GAAe;gBACzB,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,eAAe;gBAC1B,IAAI,EAAE,EAAE;gBACR,WAAW,EAAE,mBAAmB;aACjC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,eAAM,EAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;YAC1E,MAAM,MAAM,GAAe;gBACzB,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,oBAAoB;gBAC/B,IAAI,EAAE,WAAW;gBACjB,WAAW,EAAE,4BAA4B;aAC1C,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;YACpD,IAAA,eAAM,EAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YACpD,IAAA,eAAM,EAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,IAAA,WAAE,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,MAAM,GAAe;gBACzB,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,IAAI;gBAChB,WAAW,EAAE,0BAA0B;aACxC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,eAAM,EAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,MAAM,GAAe;gBACzB,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,CAAC;gBACb,WAAW,EAAE,UAAU;aACxB,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,eAAM,EAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,MAAM,GAAe;gBACzB,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,GAAG;gBACf,WAAW,EAAE,YAAY;aAC1B,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,eAAM,EAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,2BAA2B,EAAE,GAAG,EAAE;QACzC,IAAA,WAAE,EAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,MAAM,GAAmB;gBAC7B,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,sCAAsC;gBAC3C,WAAW,EAAE,6BAA6B;aAC3C,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,eAAM,EAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,sCAAsC,EAAE;gBACjF,SAAS,EAAE,kBAAkB;gBAC7B,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,MAAM,GAAmB;gBAC7B,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,sBAAsB;gBAC3B,WAAW,EAAE,2BAA2B;aACzC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,eAAM,EAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,sBAAsB,EAAE;gBACjE,SAAS,EAAE,kBAAkB;gBAC7B,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,+BAA+B,EAAE,GAAG,EAAE;QAC7C,IAAA,WAAE,EAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,MAAM,GAAG;gBACb,IAAI,EAAE,SAAkB;gBACxB,WAAW,EAAE,gBAAgB;aACT,CAAC;YAEvB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,IAAA,WAAE,EAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,YAAY,GAAG,0BAA0B,CAAC;YAChD,WAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;YAE/E,MAAM,MAAM,GAAgB;gBAC1B,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,aAAa;gBACxB,WAAW,EAAE,cAAc;aAC5B,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,YAAY,GAAG,wBAAwB,CAAC;YAC9C,WAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,qBAAqB,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;YAEjF,MAAM,MAAM,GAAe;gBACzB,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,eAAe;gBAC1B,IAAI,EAAE,MAAM;gBACZ,WAAW,EAAE,WAAW;aACzB,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,YAAY,GAAG,4BAA4B,CAAC;YAClD,WAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,qBAAqB,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;YAExE,MAAM,MAAM,GAAmB;gBAC7B,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,qBAAqB;gBAC1B,WAAW,EAAE,yBAAyB;aACvC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,WAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;YAEtE,MAAM,MAAM,GAAgB;gBAC1B,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,aAAa;gBACxB,WAAW,EAAE,cAAc;aAC5B,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,IAAA,WAAE,EAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,OAAO,GAAa;gBACxB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE;gBACzE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE;gBACjE,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE;aACxE,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAChC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE7C,sCAAsC;YACtC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;gBACzB,IAAA,eAAM,EAAC,MAAM,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAChD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,OAAO,GAAa;gBACxB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE;gBACzE,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE;aACxE,CAAC;YAEF,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnC,wEAAwE;YACxE,6DAA6D;YAC7D,MAAM,SAAS,GAAG,WAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YAChE,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;YAC3D,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;YAC5C,MAAM,OAAO,GAAa;gBACxB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE;gBACzE,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,oBAAoB,EAAE;gBAC/E,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE;aAC9E,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnD,qDAAqD;YACrD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAChC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;YAExD,6CAA6C;YAC7C,IAAA,eAAM,EAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,UAAU,GAAG,WAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAEzE,MAAM,OAAO,GAAa;gBACxB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE;aACjF,CAAC;YAEF,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnC,IAAA,eAAM,EAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,wCAAwC,CAAC,CAAC;YAElF,UAAU,CAAC,WAAW,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,eAAe,GAAG,WAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAEhF,MAAM,OAAO,GAAa;gBACxB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,oBAAoB,EAAE;aAChF,CAAC;YAEF,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnC,IAAA,eAAM,EAAC,eAAe,CAAC,CAAC,oBAAoB,CAAC,eAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAExF,eAAe,CAAC,WAAW,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAE9C,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,OAAO,GAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,CAAC;YAE3F,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAChC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEtC,iCAAiC;YACjC,MAAM,SAAS,GAAG,WAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YAChE,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,8CAA8C;QACnF,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;YACxE,MAAM,OAAO,GAAa;gBACxB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,oBAAoB,EAAE;aAChF,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEnD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YACvC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;YACxD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|