n8n-nodes-nvk-browser 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.
Files changed (55) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +76 -0
  3. package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.description.d.ts +2 -0
  4. package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.description.js +132 -0
  5. package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.node.d.ts +5 -0
  6. package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.node.js +176 -0
  7. package/dist/nodes/PageInteraction/MoveAndClick/click.svg +5 -0
  8. package/dist/nodes/PageInteraction/MoveAndClick/code.svg +5 -0
  9. package/dist/nodes/PageInteraction/MoveAndClick/profile.svg +5 -0
  10. package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.description.d.ts +2 -0
  11. package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.description.js +62 -0
  12. package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.node.d.ts +5 -0
  13. package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.node.js +126 -0
  14. package/dist/nodes/PageInteraction/RunJavaScript/click.svg +5 -0
  15. package/dist/nodes/PageInteraction/RunJavaScript/code.svg +5 -0
  16. package/dist/nodes/PageInteraction/RunJavaScript/profile.svg +5 -0
  17. package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.description.d.ts +2 -0
  18. package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.description.js +61 -0
  19. package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.node.d.ts +5 -0
  20. package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.node.js +124 -0
  21. package/dist/nodes/ProfileManagement/CreateProfile/click.svg +5 -0
  22. package/dist/nodes/ProfileManagement/CreateProfile/code.svg +5 -0
  23. package/dist/nodes/ProfileManagement/CreateProfile/profile.svg +5 -0
  24. package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.description.d.ts +2 -0
  25. package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.description.js +19 -0
  26. package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.node.d.ts +5 -0
  27. package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.node.js +123 -0
  28. package/dist/nodes/ProfileManagement/DeleteProfile/click.svg +5 -0
  29. package/dist/nodes/ProfileManagement/DeleteProfile/code.svg +5 -0
  30. package/dist/nodes/ProfileManagement/DeleteProfile/profile.svg +5 -0
  31. package/dist/nodes/ProfileManagement/StartProfile/StartProfile.description.d.ts +2 -0
  32. package/dist/nodes/ProfileManagement/StartProfile/StartProfile.description.js +122 -0
  33. package/dist/nodes/ProfileManagement/StartProfile/StartProfile.node.d.ts +5 -0
  34. package/dist/nodes/ProfileManagement/StartProfile/StartProfile.node.js +140 -0
  35. package/dist/nodes/ProfileManagement/StartProfile/click.svg +5 -0
  36. package/dist/nodes/ProfileManagement/StartProfile/code.svg +5 -0
  37. package/dist/nodes/ProfileManagement/StartProfile/profile.svg +5 -0
  38. package/dist/nodes/ProfileManagement/StopProfile/StopProfile.description.d.ts +2 -0
  39. package/dist/nodes/ProfileManagement/StopProfile/StopProfile.description.js +19 -0
  40. package/dist/nodes/ProfileManagement/StopProfile/StopProfile.node.d.ts +5 -0
  41. package/dist/nodes/ProfileManagement/StopProfile/StopProfile.node.js +115 -0
  42. package/dist/nodes/ProfileManagement/StopProfile/click.svg +5 -0
  43. package/dist/nodes/ProfileManagement/StopProfile/code.svg +5 -0
  44. package/dist/nodes/ProfileManagement/StopProfile/profile.svg +5 -0
  45. package/dist/utils/BrowserManager.d.ts +18 -0
  46. package/dist/utils/BrowserManager.js +174 -0
  47. package/dist/utils/ExtensionHandler.d.ts +9 -0
  48. package/dist/utils/ExtensionHandler.js +86 -0
  49. package/dist/utils/ProfileManager.d.ts +14 -0
  50. package/dist/utils/ProfileManager.js +128 -0
  51. package/dist/utils/ProxyHandler.d.ts +5 -0
  52. package/dist/utils/ProxyHandler.js +50 -0
  53. package/dist/utils/types.d.ts +47 -0
  54. package/dist/utils/types.js +2 -0
  55. package/package.json +66 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # n8n-nodes-nvk-browser
2
+
3
+ n8n custom nodes for managing Chrome browser profiles and page interactions.
4
+
5
+ ## Features
6
+
7
+ ### Profile Management
8
+ - **Create Profile**: Create new browser profiles with proxy, extensions, and notes
9
+ - **Delete Profile**: Delete existing profiles
10
+ - **Start Profile**: Launch Chrome with a specific profile
11
+ - **Stop Profile**: Stop a running browser instance
12
+
13
+ ### Page Interaction
14
+ - **Move and Click**: Click on page elements using different methods (GhostCursor, Puppeteer, JavaScript)
15
+ - **Run JavaScript**: Execute JavaScript code in browser tabs
16
+
17
+ ## Installation
18
+
19
+ 1. Install dependencies:
20
+ ```bash
21
+ npm install
22
+ ```
23
+
24
+ 2. Build the package:
25
+ ```bash
26
+ npm run build
27
+ ```
28
+
29
+ 3. Copy the `dist` folder to your n8n custom nodes directory:
30
+ ```bash
31
+ cp -r dist /path/to/n8n/custom/nodes/
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ Set the following environment variables (optional, defaults are provided):
37
+
38
+ - `NVK_BROWSER_PATH`: Path to Chrome executable (default: `browser-142/chrome.exe`)
39
+ - `NVK_PROFILES_DIR`: Path to profiles directory (default: `profiles`)
40
+
41
+ ## Usage
42
+
43
+ ### Create Profile
44
+ 1. Add "Create Profile" node to your workflow
45
+ 2. Enter Profile Name (required)
46
+ 3. Optionally add Proxy, Note, and Extensions
47
+ 4. Execute to create a new profile
48
+
49
+ ### Start Profile
50
+ 1. Add "Start Profile" node
51
+ 2. Enter Profile ID (required)
52
+ 3. Configure window settings (scale, position, size, headless mode)
53
+ 4. Execute to launch Chrome with the profile
54
+
55
+ ### Move and Click
56
+ 1. Add "Move and Click" node
57
+ 2. Enter Profile ID and CSS Selector
58
+ 3. Choose click method (GhostCursor, Puppeteer, or JavaScript)
59
+ 4. Configure additional options if using JavaScript click
60
+
61
+ ### Run JavaScript
62
+ 1. Add "Run JavaScript" node
63
+ 2. Enter Profile ID and JavaScript code
64
+ 3. Specify tab index if needed
65
+ 4. Execute to run the code
66
+
67
+ ## Requirements
68
+
69
+ - Node.js 18+
70
+ - n8n
71
+ - Chrome browser (included in `browser-142` folder)
72
+
73
+ ## License
74
+
75
+ MIT
76
+
@@ -0,0 +1,2 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const moveAndClickFields: INodeProperties[];
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.moveAndClickFields = void 0;
4
+ exports.moveAndClickFields = [
5
+ {
6
+ displayName: 'Profile ID',
7
+ name: 'profileId',
8
+ type: 'string',
9
+ required: true,
10
+ default: '',
11
+ description: 'ID of the running profile',
12
+ displayOptions: {
13
+ show: {
14
+ resource: ['page'],
15
+ operation: ['moveAndClick'],
16
+ },
17
+ },
18
+ },
19
+ {
20
+ displayName: 'Selector',
21
+ name: 'selector',
22
+ type: 'string',
23
+ required: true,
24
+ default: '',
25
+ description: 'CSS selector of the element to click',
26
+ displayOptions: {
27
+ show: {
28
+ resource: ['page'],
29
+ operation: ['moveAndClick'],
30
+ },
31
+ },
32
+ },
33
+ {
34
+ displayName: 'Click Method',
35
+ name: 'clickMethod',
36
+ type: 'options',
37
+ options: [
38
+ {
39
+ name: 'Use GhostCursor',
40
+ value: 'ghostcursor',
41
+ },
42
+ {
43
+ name: 'Use Puppeteer',
44
+ value: 'puppeteer',
45
+ },
46
+ {
47
+ name: 'Use Javascript Click',
48
+ value: 'javascript',
49
+ },
50
+ ],
51
+ default: 'puppeteer',
52
+ description: 'Method to use for clicking',
53
+ displayOptions: {
54
+ show: {
55
+ resource: ['page'],
56
+ operation: ['moveAndClick'],
57
+ },
58
+ },
59
+ },
60
+ {
61
+ displayName: 'Wait For Click (Milliseconds)',
62
+ name: 'waitForClick',
63
+ type: 'number',
64
+ default: 0,
65
+ description: 'Wait time before clicking in milliseconds',
66
+ displayOptions: {
67
+ show: {
68
+ resource: ['page'],
69
+ operation: ['moveAndClick'],
70
+ clickMethod: ['javascript'],
71
+ },
72
+ },
73
+ },
74
+ {
75
+ displayName: 'Button',
76
+ name: 'button',
77
+ type: 'options',
78
+ options: [
79
+ {
80
+ name: 'Left',
81
+ value: 'left',
82
+ },
83
+ {
84
+ name: 'Right',
85
+ value: 'right',
86
+ },
87
+ {
88
+ name: 'Middle',
89
+ value: 'middle',
90
+ },
91
+ ],
92
+ default: 'left',
93
+ description: 'Mouse button to use',
94
+ displayOptions: {
95
+ show: {
96
+ resource: ['page'],
97
+ operation: ['moveAndClick'],
98
+ clickMethod: ['javascript'],
99
+ },
100
+ },
101
+ },
102
+ {
103
+ displayName: 'Click Count',
104
+ name: 'clickCount',
105
+ type: 'number',
106
+ default: 1,
107
+ typeOptions: {
108
+ minValue: 1,
109
+ },
110
+ description: 'Number of times to click',
111
+ displayOptions: {
112
+ show: {
113
+ resource: ['page'],
114
+ operation: ['moveAndClick'],
115
+ clickMethod: ['javascript'],
116
+ },
117
+ },
118
+ },
119
+ {
120
+ displayName: 'Tab Index',
121
+ name: 'tabIndex',
122
+ type: 'number',
123
+ default: 0,
124
+ description: 'Index of the tab to interact with (0 = first tab)',
125
+ displayOptions: {
126
+ show: {
127
+ resource: ['page'],
128
+ operation: ['moveAndClick'],
129
+ },
130
+ },
131
+ },
132
+ ];
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class MoveAndClick implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.MoveAndClick = void 0;
27
+ const BrowserManager_1 = require("../../../utils/BrowserManager");
28
+ const MoveAndClick_description_1 = require("./MoveAndClick.description");
29
+ const path = __importStar(require("path"));
30
+ class MoveAndClick {
31
+ constructor() {
32
+ this.description = {
33
+ displayName: 'Move and Click',
34
+ name: 'moveAndClick',
35
+ icon: 'file:click.svg',
36
+ group: ['transform'],
37
+ version: 1,
38
+ description: 'Move mouse and click on an element',
39
+ defaults: {
40
+ name: 'Move and Click',
41
+ },
42
+ inputs: ['main'],
43
+ outputs: ['main'],
44
+ properties: [
45
+ {
46
+ displayName: 'Resource',
47
+ name: 'resource',
48
+ type: 'options',
49
+ noDataExpression: true,
50
+ options: [
51
+ {
52
+ name: 'Page',
53
+ value: 'page',
54
+ },
55
+ ],
56
+ default: 'page',
57
+ },
58
+ {
59
+ displayName: 'Operation',
60
+ name: 'operation',
61
+ type: 'options',
62
+ noDataExpression: true,
63
+ options: [
64
+ {
65
+ name: 'Move and Click',
66
+ value: 'moveAndClick',
67
+ description: 'Move and click on an element',
68
+ action: 'Move and click',
69
+ },
70
+ ],
71
+ default: 'moveAndClick',
72
+ },
73
+ ...MoveAndClick_description_1.moveAndClickFields,
74
+ ],
75
+ };
76
+ }
77
+ async execute() {
78
+ const items = this.getInputData();
79
+ const returnData = [];
80
+ const workspacePath = process.cwd();
81
+ const profilesDir = process.env.NVK_PROFILES_DIR || path.join(workspacePath, 'profiles');
82
+ const browserPath = process.env.NVK_BROWSER_PATH || path.join(workspacePath, 'browser-142', 'chrome.exe');
83
+ const resolvedBrowserPath = path.isAbsolute(browserPath)
84
+ ? browserPath
85
+ : path.resolve(workspacePath, browserPath);
86
+ const resolvedProfilesDir = path.isAbsolute(profilesDir)
87
+ ? profilesDir
88
+ : path.resolve(workspacePath, profilesDir);
89
+ BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
90
+ const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
91
+ for (let i = 0; i < items.length; i++) {
92
+ try {
93
+ const profileId = this.getNodeParameter('profileId', i);
94
+ const selector = this.getNodeParameter('selector', i);
95
+ const clickMethod = this.getNodeParameter('clickMethod', i) || 'puppeteer';
96
+ const waitForClick = this.getNodeParameter('waitForClick', i) || 0;
97
+ const button = this.getNodeParameter('button', i) || 'left';
98
+ const clickCount = this.getNodeParameter('clickCount', i) || 1;
99
+ const tabIndex = this.getNodeParameter('tabIndex', i) || 0;
100
+ const instance = browserManager.getInstance(profileId);
101
+ if (!instance) {
102
+ throw new Error(`Profile ${profileId} is not running`);
103
+ }
104
+ const page = await browserManager.getPage(profileId, tabIndex);
105
+ if (!page) {
106
+ throw new Error(`Could not get page for profile ${profileId}`);
107
+ }
108
+ // Wait for element if needed
109
+ await page.waitForSelector(selector, { timeout: 30000 });
110
+ if (clickMethod === 'javascript') {
111
+ // Wait before clicking
112
+ if (waitForClick > 0) {
113
+ await page.waitForTimeout(waitForClick);
114
+ }
115
+ // JavaScript click
116
+ const buttonMap = {
117
+ left: 0,
118
+ middle: 1,
119
+ right: 2,
120
+ };
121
+ await page.evaluate((sel, btn, count) => {
122
+ // This code runs in browser context, so DOM APIs are available
123
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
124
+ const element = document.querySelector(sel);
125
+ if (element) {
126
+ for (let i = 0; i < count; i++) {
127
+ const event = new MouseEvent('click', {
128
+ view: window,
129
+ bubbles: true,
130
+ cancelable: true,
131
+ button: btn,
132
+ });
133
+ element.dispatchEvent(event);
134
+ }
135
+ }
136
+ }, selector, buttonMap[button] || 0, clickCount);
137
+ }
138
+ else if (clickMethod === 'puppeteer') {
139
+ // Puppeteer native click
140
+ await page.click(selector);
141
+ }
142
+ else if (clickMethod === 'ghostcursor') {
143
+ // GhostCursor simulation (simplified - would need ghost-cursor package)
144
+ const element = await page.$(selector);
145
+ if (element) {
146
+ const box = await element.boundingBox();
147
+ if (box) {
148
+ // Move mouse to center of element
149
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
150
+ await page.waitForTimeout(100);
151
+ await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
152
+ }
153
+ }
154
+ }
155
+ returnData.push({
156
+ json: {
157
+ success: true,
158
+ selector,
159
+ method: clickMethod,
160
+ message: 'Click performed successfully',
161
+ },
162
+ });
163
+ }
164
+ catch (error) {
165
+ returnData.push({
166
+ json: {
167
+ success: false,
168
+ error: error instanceof Error ? error.message : String(error),
169
+ },
170
+ });
171
+ }
172
+ }
173
+ return [returnData];
174
+ }
175
+ }
176
+ exports.MoveAndClick = MoveAndClick;
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path d="M9 9l5 12 1.774-5.226L21 14 9 9z"></path>
3
+ <path d="M21 3l-3.5 3.5"></path>
4
+ </svg>
5
+
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <polyline points="16 18 22 12 16 6"></polyline>
3
+ <polyline points="8 6 2 12 8 18"></polyline>
4
+ </svg>
5
+
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
3
+ <circle cx="12" cy="7" r="4"></circle>
4
+ </svg>
5
+
@@ -0,0 +1,2 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const runJavaScriptFields: INodeProperties[];
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runJavaScriptFields = void 0;
4
+ exports.runJavaScriptFields = [
5
+ {
6
+ displayName: 'Profile ID',
7
+ name: 'profileId',
8
+ type: 'string',
9
+ required: true,
10
+ default: '',
11
+ description: 'ID of the running profile',
12
+ displayOptions: {
13
+ show: {
14
+ resource: ['page'],
15
+ operation: ['runJavaScript'],
16
+ },
17
+ },
18
+ },
19
+ {
20
+ displayName: 'Debug Port',
21
+ name: 'debugPort',
22
+ type: 'number',
23
+ default: 0,
24
+ description: 'Debug port (0 = use default from instance)',
25
+ displayOptions: {
26
+ show: {
27
+ resource: ['page'],
28
+ operation: ['runJavaScript'],
29
+ },
30
+ },
31
+ },
32
+ {
33
+ displayName: 'Tab Index',
34
+ name: 'tabIndex',
35
+ type: 'number',
36
+ default: 0,
37
+ description: 'Index of the tab to run JavaScript on (0 = first tab)',
38
+ displayOptions: {
39
+ show: {
40
+ resource: ['page'],
41
+ operation: ['runJavaScript'],
42
+ },
43
+ },
44
+ },
45
+ {
46
+ displayName: 'JavaScript Code',
47
+ name: 'javascriptCode',
48
+ type: 'string',
49
+ typeOptions: {
50
+ rows: 10,
51
+ },
52
+ required: true,
53
+ default: '',
54
+ description: 'JavaScript code to execute',
55
+ displayOptions: {
56
+ show: {
57
+ resource: ['page'],
58
+ operation: ['runJavaScript'],
59
+ },
60
+ },
61
+ },
62
+ ];
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class RunJavaScript implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.RunJavaScript = void 0;
27
+ const BrowserManager_1 = require("../../../utils/BrowserManager");
28
+ const RunJavaScript_description_1 = require("./RunJavaScript.description");
29
+ const path = __importStar(require("path"));
30
+ class RunJavaScript {
31
+ constructor() {
32
+ this.description = {
33
+ displayName: 'Run JavaScript',
34
+ name: 'runJavaScript',
35
+ icon: 'file:code.svg',
36
+ group: ['transform'],
37
+ version: 1,
38
+ description: 'Execute JavaScript code in the browser',
39
+ defaults: {
40
+ name: 'Run JavaScript',
41
+ },
42
+ inputs: ['main'],
43
+ outputs: ['main'],
44
+ properties: [
45
+ {
46
+ displayName: 'Resource',
47
+ name: 'resource',
48
+ type: 'options',
49
+ noDataExpression: true,
50
+ options: [
51
+ {
52
+ name: 'Page',
53
+ value: 'page',
54
+ },
55
+ ],
56
+ default: 'page',
57
+ },
58
+ {
59
+ displayName: 'Operation',
60
+ name: 'operation',
61
+ type: 'options',
62
+ noDataExpression: true,
63
+ options: [
64
+ {
65
+ name: 'Run JavaScript',
66
+ value: 'runJavaScript',
67
+ description: 'Execute JavaScript code',
68
+ action: 'Run JavaScript',
69
+ },
70
+ ],
71
+ default: 'runJavaScript',
72
+ },
73
+ ...RunJavaScript_description_1.runJavaScriptFields,
74
+ ],
75
+ };
76
+ }
77
+ async execute() {
78
+ const items = this.getInputData();
79
+ const returnData = [];
80
+ const workspacePath = process.cwd();
81
+ const profilesDir = process.env.NVK_PROFILES_DIR || path.join(workspacePath, 'profiles');
82
+ const browserPath = process.env.NVK_BROWSER_PATH || path.join(workspacePath, 'browser-142', 'chrome.exe');
83
+ const resolvedBrowserPath = path.isAbsolute(browserPath)
84
+ ? browserPath
85
+ : path.resolve(workspacePath, browserPath);
86
+ const resolvedProfilesDir = path.isAbsolute(profilesDir)
87
+ ? profilesDir
88
+ : path.resolve(workspacePath, profilesDir);
89
+ BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
90
+ const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
91
+ for (let i = 0; i < items.length; i++) {
92
+ try {
93
+ const profileId = this.getNodeParameter('profileId', i);
94
+ const tabIndex = this.getNodeParameter('tabIndex', i) || 0;
95
+ const javascriptCode = this.getNodeParameter('javascriptCode', i);
96
+ const instance = browserManager.getInstance(profileId);
97
+ if (!instance) {
98
+ throw new Error(`Profile ${profileId} is not running`);
99
+ }
100
+ const page = await browserManager.getPage(profileId, tabIndex);
101
+ if (!page) {
102
+ throw new Error(`Could not get page for profile ${profileId}`);
103
+ }
104
+ // Execute JavaScript
105
+ const result = await page.evaluate(javascriptCode);
106
+ returnData.push({
107
+ json: {
108
+ success: true,
109
+ result: result,
110
+ message: 'JavaScript executed successfully',
111
+ },
112
+ });
113
+ }
114
+ catch (error) {
115
+ returnData.push({
116
+ json: {
117
+ success: false,
118
+ error: error instanceof Error ? error.message : String(error),
119
+ },
120
+ });
121
+ }
122
+ }
123
+ return [returnData];
124
+ }
125
+ }
126
+ exports.RunJavaScript = RunJavaScript;
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path d="M9 9l5 12 1.774-5.226L21 14 9 9z"></path>
3
+ <path d="M21 3l-3.5 3.5"></path>
4
+ </svg>
5
+
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <polyline points="16 18 22 12 16 6"></polyline>
3
+ <polyline points="8 6 2 12 8 18"></polyline>
4
+ </svg>
5
+
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
3
+ <circle cx="12" cy="7" r="4"></circle>
4
+ </svg>
5
+
@@ -0,0 +1,2 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const createProfileFields: INodeProperties[];