n8n-nodes-nvk-browser 1.0.0 → 1.0.1

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.
@@ -129,4 +129,17 @@ exports.moveAndClickFields = [
129
129
  },
130
130
  },
131
131
  },
132
+ {
133
+ displayName: 'Auto Start Profile',
134
+ name: 'autoStart',
135
+ type: 'boolean',
136
+ default: false,
137
+ description: 'Automatically start the profile if it is not running',
138
+ displayOptions: {
139
+ show: {
140
+ resource: ['page'],
141
+ operation: ['moveAndClick'],
142
+ },
143
+ },
144
+ },
132
145
  ];
@@ -86,7 +86,6 @@ class MoveAndClick {
86
86
  const resolvedProfilesDir = path.isAbsolute(profilesDir)
87
87
  ? profilesDir
88
88
  : path.resolve(workspacePath, profilesDir);
89
- BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
90
89
  const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
91
90
  for (let i = 0; i < items.length; i++) {
92
91
  try {
@@ -97,9 +96,14 @@ class MoveAndClick {
97
96
  const button = this.getNodeParameter('button', i) || 'left';
98
97
  const clickCount = this.getNodeParameter('clickCount', i) || 1;
99
98
  const tabIndex = this.getNodeParameter('tabIndex', i) || 0;
100
- const instance = browserManager.getInstance(profileId);
99
+ const autoStart = this.getNodeParameter('autoStart', i) || false;
100
+ let instance = browserManager.getInstance(profileId);
101
+ // Auto start profile nếu chưa chạy và option được bật
102
+ if (!instance && autoStart) {
103
+ instance = await browserManager.startProfileIfNotRunning(profileId);
104
+ }
101
105
  if (!instance) {
102
- throw new Error(`Profile ${profileId} is not running`);
106
+ throw new Error(`Profile ${profileId} is not running. Enable "Auto Start Profile" to start it automatically.`);
103
107
  }
104
108
  const page = await browserManager.getPage(profileId, tabIndex);
105
109
  if (!page) {
@@ -59,4 +59,17 @@ exports.runJavaScriptFields = [
59
59
  },
60
60
  },
61
61
  },
62
+ {
63
+ displayName: 'Auto Start Profile',
64
+ name: 'autoStart',
65
+ type: 'boolean',
66
+ default: false,
67
+ description: 'Automatically start the profile if it is not running',
68
+ displayOptions: {
69
+ show: {
70
+ resource: ['page'],
71
+ operation: ['runJavaScript'],
72
+ },
73
+ },
74
+ },
62
75
  ];
@@ -86,16 +86,20 @@ class RunJavaScript {
86
86
  const resolvedProfilesDir = path.isAbsolute(profilesDir)
87
87
  ? profilesDir
88
88
  : path.resolve(workspacePath, profilesDir);
89
- BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
90
89
  const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
91
90
  for (let i = 0; i < items.length; i++) {
92
91
  try {
93
92
  const profileId = this.getNodeParameter('profileId', i);
94
93
  const tabIndex = this.getNodeParameter('tabIndex', i) || 0;
95
94
  const javascriptCode = this.getNodeParameter('javascriptCode', i);
96
- const instance = browserManager.getInstance(profileId);
95
+ const autoStart = this.getNodeParameter('autoStart', i) || false;
96
+ let instance = browserManager.getInstance(profileId);
97
+ // Auto start profile nếu chưa chạy và option được bật
98
+ if (!instance && autoStart) {
99
+ instance = await browserManager.startProfileIfNotRunning(profileId);
100
+ }
97
101
  if (!instance) {
98
- throw new Error(`Profile ${profileId} is not running`);
102
+ throw new Error(`Profile ${profileId} is not running. Enable "Auto Start Profile" to start it automatically.`);
99
103
  }
100
104
  const page = await browserManager.getPage(profileId, tabIndex);
101
105
  if (!page) {
@@ -88,7 +88,6 @@ class DeleteProfile {
88
88
  ? profilesDir
89
89
  : path.resolve(workspacePath, profilesDir);
90
90
  const profileManager = new ProfileManager_1.ProfileManager(resolvedProfilesDir);
91
- BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
92
91
  const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
93
92
  for (let i = 0; i < items.length; i++) {
94
93
  try {
@@ -87,7 +87,6 @@ class StartProfile {
87
87
  const resolvedProfilesDir = path.isAbsolute(profilesDir)
88
88
  ? profilesDir
89
89
  : path.resolve(workspacePath, profilesDir);
90
- BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
91
90
  const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
92
91
  for (let i = 0; i < items.length; i++) {
93
92
  try {
@@ -86,7 +86,6 @@ class StopProfile {
86
86
  const resolvedProfilesDir = path.isAbsolute(profilesDir)
87
87
  ? profilesDir
88
88
  : path.resolve(workspacePath, profilesDir);
89
- BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
90
89
  const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
91
90
  for (let i = 0; i < items.length; i++) {
92
91
  try {
@@ -1,13 +1,14 @@
1
1
  import { Page } from 'puppeteer-core';
2
2
  import { BrowserInstance, WindowConfig } from './types';
3
3
  export declare class BrowserManager {
4
- private static instance;
4
+ private static instances;
5
5
  private instances;
6
6
  private browserPath;
7
7
  private profilesManager;
8
+ private profilesDir;
8
9
  private constructor();
9
- static getInstance(browserPath?: string, profilesDir?: string): BrowserManager;
10
- static resetInstance(): void;
10
+ static getInstance(browserPath: string, profilesDir: string): BrowserManager;
11
+ static resetInstance(browserPath?: string, profilesDir?: string): void;
11
12
  startProfile(profileId: string, windowConfig?: WindowConfig): Promise<BrowserInstance>;
12
13
  stopProfile(profileId: string): Promise<boolean>;
13
14
  getInstance(profileId: string): BrowserInstance | undefined;
@@ -15,4 +16,6 @@ export declare class BrowserManager {
15
16
  private getDebugPort;
16
17
  private setWindowPosition;
17
18
  getPage(profileId: string, tabIndex?: number): Promise<Page | null>;
19
+ private setProfileName;
20
+ startProfileIfNotRunning(profileId: string, windowConfig?: WindowConfig): Promise<BrowserInstance>;
18
21
  }
@@ -1,9 +1,34 @@
1
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
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
29
  exports.BrowserManager = void 0;
30
+ const path = __importStar(require("path"));
31
+ const fs = __importStar(require("fs"));
7
32
  const puppeteer_core_1 = __importDefault(require("puppeteer-core"));
8
33
  const ProxyHandler_1 = require("./ProxyHandler");
9
34
  const ExtensionHandler_1 = require("./ExtensionHandler");
@@ -12,19 +37,25 @@ class BrowserManager {
12
37
  constructor(browserPath, profilesDir) {
13
38
  this.instances = new Map();
14
39
  this.browserPath = browserPath;
40
+ this.profilesDir = profilesDir;
15
41
  this.profilesManager = new ProfileManager_1.ProfileManager(profilesDir);
16
42
  }
17
43
  static getInstance(browserPath, profilesDir) {
18
- if (!BrowserManager.instance) {
19
- if (!browserPath || !profilesDir) {
20
- throw new Error('BrowserManager must be initialized with browserPath and profilesDir');
21
- }
22
- BrowserManager.instance = new BrowserManager(browserPath, profilesDir);
44
+ // Tạo key từ paths để có thể có nhiều BrowserManager với paths khác nhau
45
+ const key = `${browserPath}:${profilesDir}`;
46
+ if (!BrowserManager.instances.has(key)) {
47
+ BrowserManager.instances.set(key, new BrowserManager(browserPath, profilesDir));
23
48
  }
24
- return BrowserManager.instance;
49
+ return BrowserManager.instances.get(key);
25
50
  }
26
- static resetInstance() {
27
- BrowserManager.instance = null;
51
+ static resetInstance(browserPath, profilesDir) {
52
+ if (browserPath && profilesDir) {
53
+ const key = `${browserPath}:${profilesDir}`;
54
+ BrowserManager.instances.delete(key);
55
+ }
56
+ else {
57
+ BrowserManager.instances.clear();
58
+ }
28
59
  }
29
60
  async startProfile(profileId, windowConfig) {
30
61
  // Kiểm tra xem profile đã đang chạy chưa
@@ -37,6 +68,8 @@ class BrowserManager {
37
68
  throw new Error(`Profile ${profileId} not found`);
38
69
  }
39
70
  const profilePath = this.profilesManager.getProfilePath(profileId);
71
+ // Set profile name trong Chrome preferences
72
+ this.setProfileName(profilePath, profile.name);
40
73
  // Chuẩn bị Chrome arguments
41
74
  const args = [
42
75
  `--user-data-dir=${profilePath}`,
@@ -169,6 +202,64 @@ class BrowserManager {
169
202
  instance.pages.set(tabIndex, newPage);
170
203
  return newPage;
171
204
  }
205
+ setProfileName(profilePath, profileName) {
206
+ try {
207
+ // Set profile name trong Local State file
208
+ const localStatePath = path.join(profilePath, 'Local State');
209
+ let localState = {};
210
+ if (fs.existsSync(localStatePath)) {
211
+ try {
212
+ const content = fs.readFileSync(localStatePath, 'utf-8');
213
+ localState = JSON.parse(content);
214
+ }
215
+ catch (error) {
216
+ // File exists but invalid JSON, start fresh
217
+ }
218
+ }
219
+ // Set profile name trong Chrome profile structure
220
+ if (!localState.profile) {
221
+ localState.profile = {};
222
+ }
223
+ localState.profile.name = profileName;
224
+ // Cũng set trong profile info
225
+ if (!localState.profile.info_cache) {
226
+ localState.profile.info_cache = {};
227
+ }
228
+ localState.profile.info_cache.Default = {
229
+ name: profileName,
230
+ is_using_default_name: false,
231
+ };
232
+ // Write back
233
+ fs.writeFileSync(localStatePath, JSON.stringify(localState, null, 2), 'utf-8');
234
+ // Cũng set trong Preferences file nếu có
235
+ const prefsPath = path.join(profilePath, 'Default', 'Preferences');
236
+ if (fs.existsSync(prefsPath)) {
237
+ try {
238
+ const prefsContent = fs.readFileSync(prefsPath, 'utf-8');
239
+ const prefs = JSON.parse(prefsContent);
240
+ if (!prefs.profile) {
241
+ prefs.profile = {};
242
+ }
243
+ prefs.profile.name = profileName;
244
+ fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
245
+ }
246
+ catch (error) {
247
+ // Ignore preferences errors
248
+ }
249
+ }
250
+ }
251
+ catch (error) {
252
+ // Ignore errors - profile name is optional
253
+ }
254
+ }
255
+ async startProfileIfNotRunning(profileId, windowConfig) {
256
+ // Kiểm tra xem profile đã đang chạy chưa
257
+ if (this.instances.has(profileId)) {
258
+ return this.instances.get(profileId);
259
+ }
260
+ // Nếu chưa chạy, start nó
261
+ return await this.startProfile(profileId, windowConfig);
262
+ }
172
263
  }
173
- BrowserManager.instance = null;
264
+ BrowserManager.instances = new Map();
174
265
  exports.BrowserManager = BrowserManager;
@@ -6,6 +6,7 @@ export declare class ProfileManager {
6
6
  private getProfileMetadataPath;
7
7
  private getProfileDir;
8
8
  createProfile(name: string, proxy?: string, note?: string, extensions?: string[]): ProfileData;
9
+ private setProfileName;
9
10
  getProfile(profileId: string): ProfileData | null;
10
11
  getAllProfiles(): ProfileData[];
11
12
  updateProfile(profileId: string, updates: Partial<ProfileData>): ProfileData | null;
@@ -60,8 +60,66 @@ class ProfileManager {
60
60
  // Lưu metadata
61
61
  const metadataPath = this.getProfileMetadataPath(profileId);
62
62
  fs.writeFileSync(metadataPath, JSON.stringify(profileData, null, 2), 'utf-8');
63
+ // Set profile name trong Chrome files ngay khi tạo
64
+ this.setProfileName(profileDir, name);
63
65
  return profileData;
64
66
  }
67
+ setProfileName(profilePath, profileName) {
68
+ try {
69
+ // Set profile name trong Local State file
70
+ const localStatePath = path.join(profilePath, 'Local State');
71
+ let localState = {};
72
+ if (fs.existsSync(localStatePath)) {
73
+ try {
74
+ const content = fs.readFileSync(localStatePath, 'utf-8');
75
+ localState = JSON.parse(content);
76
+ }
77
+ catch (error) {
78
+ // File exists but invalid JSON, start fresh
79
+ }
80
+ }
81
+ // Set profile name trong Chrome profile structure
82
+ if (!localState.profile) {
83
+ localState.profile = {};
84
+ }
85
+ localState.profile.name = profileName;
86
+ // Cũng set trong profile info
87
+ if (!localState.profile.info_cache) {
88
+ localState.profile.info_cache = {};
89
+ }
90
+ localState.profile.info_cache.Default = {
91
+ name: profileName,
92
+ is_using_default_name: false,
93
+ };
94
+ // Write back
95
+ fs.writeFileSync(localStatePath, JSON.stringify(localState, null, 2), 'utf-8');
96
+ // Tạo Default directory nếu chưa có
97
+ const defaultDir = path.join(profilePath, 'Default');
98
+ if (!fs.existsSync(defaultDir)) {
99
+ fs.mkdirSync(defaultDir, { recursive: true });
100
+ }
101
+ // Cũng set trong Preferences file
102
+ const prefsPath = path.join(defaultDir, 'Preferences');
103
+ let prefs = {};
104
+ if (fs.existsSync(prefsPath)) {
105
+ try {
106
+ const prefsContent = fs.readFileSync(prefsPath, 'utf-8');
107
+ prefs = JSON.parse(prefsContent);
108
+ }
109
+ catch (error) {
110
+ // Ignore
111
+ }
112
+ }
113
+ if (!prefs.profile) {
114
+ prefs.profile = {};
115
+ }
116
+ prefs.profile.name = profileName;
117
+ fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
118
+ }
119
+ catch (error) {
120
+ // Ignore errors - profile name is optional
121
+ }
122
+ }
65
123
  getProfile(profileId) {
66
124
  const metadataPath = this.getProfileMetadataPath(profileId);
67
125
  if (!fs.existsSync(metadataPath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-browser",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "n8n nodes for managing Chrome browser profiles and page interactions",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",