n8n-nodes-nvk-browser 1.0.78 → 1.0.80

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.
@@ -95,29 +95,76 @@ class CreateProfile {
95
95
  const extensionsInput = this.getNodeParameter('extensions', i) || '';
96
96
  // Parse extensions
97
97
  const extensions = ExtensionHandler_1.ExtensionHandler.parseExtensions(extensionsInput);
98
- // Validate extensions lấy resolved paths
99
- const extensionPaths = [];
98
+ // Tạo profile trước (để profilePath)
99
+ const profile = profileManager.createProfile(profileName, proxy, note, extensions, [] // extensionPaths sẽ được set sau
100
+ );
101
+ const profilePath = profileManager.getProfilePath(profile.id);
102
+ // Validate và install extensions vào profile
103
+ const extensionInfos = [];
100
104
  if (extensions.length > 0) {
101
- console.log(`[CreateProfile] Validating ${extensions.length} extension(s)...`);
105
+ console.log(`[CreateProfile] ==========================================`);
106
+ console.log(`[CreateProfile] Validating and installing ${extensions.length} extension(s)...`);
107
+ console.log(`[CreateProfile] Profile path: ${profilePath}`);
108
+ console.log(`[CreateProfile] ==========================================`);
102
109
  for (const ext of extensions) {
103
110
  try {
104
- const validation = await ExtensionHandler_1.ExtensionHandler.validateExtension(ext, resolvedProfilesDir);
105
- if (validation.valid && validation.path) {
106
- extensionPaths.push(validation.path);
107
- console.log(`[CreateProfile] Extension validated: ${ext} -> ${validation.path}`);
111
+ console.log(`[CreateProfile] Processing extension: ${ext}`);
112
+ // Validate extension lấy info
113
+ const validation = await ExtensionHandler_1.ExtensionHandler.validateAndGetExtensionInfo(ext, resolvedProfilesDir);
114
+ console.log(`[CreateProfile] Validation result:`, {
115
+ valid: validation.valid,
116
+ path: validation.path,
117
+ extensionId: validation.extensionId,
118
+ version: validation.version,
119
+ error: validation.error,
120
+ });
121
+ if (validation.valid && validation.path && validation.extensionId) {
122
+ // Install extension vào profile Extensions directory
123
+ console.log(`[CreateProfile] Installing extension to profile...`);
124
+ const extensionInfo = await ExtensionHandler_1.ExtensionHandler.installExtensionToProfile(validation.path, profilePath, ext);
125
+ extensionInfos.push({
126
+ extensionId: extensionInfo.extensionId,
127
+ version: extensionInfo.version,
128
+ path: extensionInfo.path,
129
+ manifest: extensionInfo.manifest,
130
+ });
131
+ console.log(`[CreateProfile] ✓ Extension installed successfully: ${ext} -> ${extensionInfo.extensionId} v${extensionInfo.version}`);
132
+ console.log(`[CreateProfile] Installed at: ${extensionInfo.path}`);
108
133
  }
109
134
  else {
110
- console.error(`[CreateProfile] ✗ Extension validation failed: ${ext} - ${validation.error || 'Unknown error'}`);
135
+ console.error(`[CreateProfile] ✗ Extension validation failed: ${ext}`);
136
+ console.error(`[CreateProfile] Error: ${validation.error || 'Unknown error'}`);
137
+ console.error(`[CreateProfile] Valid: ${validation.valid}, Path: ${validation.path}, ExtensionId: ${validation.extensionId}`);
111
138
  }
112
139
  }
113
140
  catch (error) {
114
- console.error(`[CreateProfile] ✗ Error validating extension "${ext}":`, error);
141
+ console.error(`[CreateProfile] ✗ ERROR installing extension "${ext}":`, error);
142
+ console.error(`[CreateProfile] Stack: ${error instanceof Error ? error.stack : 'N/A'}`);
115
143
  }
116
144
  }
117
- console.log(`[CreateProfile] Successfully validated ${extensionPaths.length}/${extensions.length} extension(s)`);
145
+ console.log(`[CreateProfile] ==========================================`);
146
+ console.log(`[CreateProfile] Installation summary: ${extensionInfos.length}/${extensions.length} extension(s) installed`);
147
+ console.log(`[CreateProfile] ==========================================`);
148
+ // Lưu extension configuration vào Preferences
149
+ if (extensionInfos.length > 0) {
150
+ try {
151
+ console.log(`[CreateProfile] Saving extension configurations to Preferences...`);
152
+ profileManager.saveExtensionsToPreferences(profilePath, extensionInfos);
153
+ console.log(`[CreateProfile] ✓ Extension configurations saved to Preferences`);
154
+ }
155
+ catch (error) {
156
+ console.error(`[CreateProfile] ✗ ERROR saving extension configurations:`, error);
157
+ console.error(`[CreateProfile] Stack: ${error instanceof Error ? error.stack : 'N/A'}`);
158
+ // Không throw - chỉ log error để user biết
159
+ }
160
+ }
161
+ else {
162
+ console.warn(`[CreateProfile] ⚠ No extensions were installed successfully. Check logs above for errors.`);
163
+ }
164
+ }
165
+ else {
166
+ console.log(`[CreateProfile] No extensions to install`);
118
167
  }
119
- // Tạo profile với cả extensionPaths đã resolve
120
- const profile = profileManager.createProfile(profileName, proxy, note, extensions, extensionPaths);
121
168
  // Lưu proxy authentication vào profile nếu có
122
169
  // QUAN TRỌNG: Phải gọi SAU khi createProfile và setProfileName để Preferences file đã được tạo
123
170
  // Nhưng phải đảm bảo không bị ghi đè
@@ -192,6 +239,22 @@ class CreateProfile {
192
239
  else {
193
240
  console.log(`[CreateProfile] No proxy provided`);
194
241
  }
242
+ // Kiểm tra xem extensions đã được install chưa
243
+ const extensionsDir = path.join(profilePath, 'Default', 'Extensions');
244
+ const hasExtensionsDir = fs.existsSync(extensionsDir);
245
+ let installedExtensionsCount = 0;
246
+ if (hasExtensionsDir) {
247
+ try {
248
+ const extensionDirs = fs.readdirSync(extensionsDir).filter((dir) => {
249
+ const dirPath = path.join(extensionsDir, dir);
250
+ return fs.statSync(dirPath).isDirectory();
251
+ });
252
+ installedExtensionsCount = extensionDirs.length;
253
+ }
254
+ catch (error) {
255
+ // Ignore
256
+ }
257
+ }
195
258
  returnData.push({
196
259
  json: {
197
260
  success: true,
@@ -203,6 +266,8 @@ class CreateProfile {
203
266
  extensions: profile.extensions,
204
267
  extensionPaths: profile.extensionPaths,
205
268
  createdAt: profile.createdAt,
269
+ installedExtensionsCount: installedExtensionsCount,
270
+ extensionsDirectoryExists: hasExtensionsDir,
206
271
  },
207
272
  },
208
273
  });
@@ -31,7 +31,6 @@ const path = __importStar(require("path"));
31
31
  const fs = __importStar(require("fs"));
32
32
  const puppeteer_core_1 = __importDefault(require("puppeteer-core"));
33
33
  const ProxyHandler_1 = require("./ProxyHandler");
34
- const ExtensionHandler_1 = require("./ExtensionHandler");
35
34
  const ProfileManager_1 = require("./ProfileManager");
36
35
  class BrowserManager {
37
36
  constructor(browserPath, profilesDir) {
@@ -91,43 +90,27 @@ class BrowserManager {
91
90
  '--disable-dev-shm-usage',
92
91
  '--no-sandbox',
93
92
  '--disable-setuid-sandbox',
93
+ '--enable-extensions',
94
+ '--extensions-on-chrome-urls',
95
+ '--disable-features=ExtensionsMenuInAppMenu', // Đảm bảo extensions menu hoạt động
94
96
  ];
95
97
  // Proxy configuration - KHÔNG bao gồm credentials trong URL
96
98
  if (proxyConfig) {
97
99
  args.push(...ProxyHandler_1.ProxyHandler.getChromeProxyArgs(proxyConfig));
98
100
  }
99
101
  // Extension configuration
100
- // Sử dụng extensionPaths đã resolve thay validate lại
101
- if (profile.extensionPaths && profile.extensionPaths.length > 0) {
102
- // Kiểm tra paths còn tồn tại không
103
- const validPaths = [];
104
- for (const extPath of profile.extensionPaths) {
105
- const manifestPath = path.join(extPath, 'manifest.json');
106
- if (fs.existsSync(manifestPath)) {
107
- validPaths.push(extPath);
108
- }
109
- else {
110
- console.warn(`[BrowserManager] Extension path no longer valid (missing manifest.json): ${extPath}`);
111
- }
112
- }
113
- if (validPaths.length > 0) {
114
- args.push(`--load-extension=${validPaths.join(',')}`);
115
- console.log(`[BrowserManager] Loading ${validPaths.length} extension(s) from cached paths`);
116
- }
117
- else {
118
- console.warn(`[BrowserManager] No valid extension paths found, skipping extensions`);
119
- }
120
- }
121
- else if (profile.extensions && profile.extensions.length > 0) {
122
- // Fallback: Nếu không có extensionPaths, validate lại (cho backward compatibility)
123
- console.log(`[BrowserManager] No extensionPaths found, validating extensions on-the-fly (legacy mode)`);
124
- try {
125
- const extensionArgs = await ExtensionHandler_1.ExtensionHandler.getExtensionArgs(profile.extensions, this.profilesDir);
126
- args.push(...extensionArgs);
127
- }
128
- catch (error) {
129
- console.error(`[BrowserManager] Error loading extensions:`, error);
130
- // Continue without extensions rather than failing completely
102
+ // Extensions đã được install vào profile/Default/Extensions/ nên không cần --load-extension
103
+ // Chrome sẽ tự động load từ Preferences và Extensions directory
104
+ const extensionsDir = path.join(profilePath, 'Default', 'Extensions');
105
+ if (fs.existsSync(extensionsDir)) {
106
+ const extensionDirs = fs.readdirSync(extensionsDir).filter((dir) => {
107
+ const dirPath = path.join(extensionsDir, dir);
108
+ return fs.statSync(dirPath).isDirectory();
109
+ });
110
+ if (extensionDirs.length > 0) {
111
+ console.log(`[BrowserManager] Found ${extensionDirs.length} extension(s) installed in profile Extensions directory`);
112
+ // Extensions sẽ được load tự động từ Preferences và Extensions directory
113
+ // Không cần --load-extension vì đã có trong profile
131
114
  }
132
115
  }
133
116
  // Window configuration
@@ -3,6 +3,14 @@ interface ExtensionValidationResult {
3
3
  path?: string;
4
4
  error?: string;
5
5
  type?: 'store-id' | 'url' | 'local-path';
6
+ extensionId?: string;
7
+ version?: string;
8
+ }
9
+ interface ExtensionInfo {
10
+ extensionId: string;
11
+ version: string;
12
+ path: string;
13
+ manifest: any;
6
14
  }
7
15
  export declare class ExtensionHandler {
8
16
  private static readonly CACHE_DIR_NAME;
@@ -62,6 +70,40 @@ export declare class ExtensionHandler {
62
70
  * Download và extract extension từ URL
63
71
  */
64
72
  private static downloadAndExtractExtensionFromUrl;
73
+ /**
74
+ * Đọc và parse manifest.json từ extension path
75
+ */
76
+ private static readManifest;
77
+ /**
78
+ * Lấy extension ID từ manifest.json
79
+ * Nếu là Chrome Store ID thì dùng ID đó, nếu không thì generate từ manifest key
80
+ */
81
+ private static getExtensionId;
82
+ /**
83
+ * Lấy version từ manifest.json
84
+ */
85
+ private static getExtensionVersion;
86
+ /**
87
+ * Copy extension vào profile Extensions directory
88
+ * Structure: {profile}/Default/Extensions/{extension-id}/{version}/
89
+ */
90
+ static installExtensionToProfile(extensionPath: string, profilePath: string, originalExtensionInput?: string): Promise<ExtensionInfo>;
91
+ /**
92
+ * Copy directory recursively
93
+ */
94
+ private static copyDirectory;
95
+ /**
96
+ * Tạo verified_contents.json structure
97
+ */
98
+ private static createVerifiedContents;
99
+ /**
100
+ * Validate extension và lấy thông tin chi tiết
101
+ */
102
+ static validateAndGetExtensionInfo(extension: string, workspacePath?: string): Promise<ExtensionValidationResult & {
103
+ extensionId?: string;
104
+ version?: string;
105
+ manifest?: any;
106
+ }>;
65
107
  /**
66
108
  * Get Chrome arguments để load extensions
67
109
  */
@@ -509,6 +509,202 @@ class ExtensionHandler {
509
509
  throw error;
510
510
  }
511
511
  }
512
+ /**
513
+ * Đọc và parse manifest.json từ extension path
514
+ */
515
+ static readManifest(extensionPath) {
516
+ const manifestPath = path.join(extensionPath, 'manifest.json');
517
+ if (!fs.existsSync(manifestPath)) {
518
+ throw new Error('manifest.json not found');
519
+ }
520
+ const content = fs.readFileSync(manifestPath, 'utf-8');
521
+ return JSON.parse(content);
522
+ }
523
+ /**
524
+ * Lấy extension ID từ manifest.json
525
+ * Nếu là Chrome Store ID thì dùng ID đó, nếu không thì generate từ manifest key
526
+ */
527
+ static getExtensionId(manifest, originalExtensionInput) {
528
+ // Nếu input là Chrome Store ID (32 chars), dùng nó
529
+ if (originalExtensionInput && /^[a-z0-9]{32}$/i.test(originalExtensionInput)) {
530
+ return originalExtensionInput.toLowerCase();
531
+ }
532
+ // Nếu manifest có key (public key), generate ID từ key theo cách Chrome làm
533
+ if (manifest.key) {
534
+ try {
535
+ // Chrome extension ID được generate từ public key PEM
536
+ // Format: Base32 encoding của first 128 bits (16 bytes) của SHA256(public key)
537
+ let keyData = manifest.key;
538
+ let keyString = '';
539
+ // Nếu key là string (PEM format hoặc base64)
540
+ if (typeof keyData === 'string') {
541
+ keyString = keyData;
542
+ }
543
+ else if (Array.isArray(keyData) || Buffer.isBuffer(keyData)) {
544
+ keyString = Buffer.from(keyData).toString('base64');
545
+ }
546
+ if (keyString) {
547
+ // Parse PEM nếu có
548
+ if (keyString.includes('-----BEGIN')) {
549
+ // Extract base64 content từ PEM
550
+ const pemMatch = keyString.match(/-----BEGIN[^-]+-----([\s\S]*)-----END[^-]+-----/);
551
+ if (pemMatch) {
552
+ keyString = pemMatch[1].replace(/\s/g, '');
553
+ }
554
+ }
555
+ // SHA256 hash của public key
556
+ const hash = crypto.createHash('sha256').update(keyString).digest();
557
+ // Base32 encode first 16 bytes (128 bits)
558
+ const base32Chars = 'abcdefghijklmnopqrstuvwxyz234567';
559
+ let extensionId = '';
560
+ let bits = 0;
561
+ let value = 0;
562
+ for (let i = 0; i < 16; i++) {
563
+ value = (value << 8) | hash[i];
564
+ bits += 8;
565
+ while (bits >= 5) {
566
+ extensionId += base32Chars[(value >>> (bits - 5)) & 31];
567
+ bits -= 5;
568
+ }
569
+ }
570
+ if (bits > 0) {
571
+ extensionId += base32Chars[(value << (5 - bits)) & 31];
572
+ }
573
+ // Chrome extension ID luôn là 32 ký tự
574
+ return extensionId.substring(0, 32).toLowerCase();
575
+ }
576
+ }
577
+ catch (error) {
578
+ console.warn(`[ExtensionHandler] Could not generate ID from key:`, error);
579
+ }
580
+ }
581
+ // Fallback: Generate ID từ manifest (name + path hash) - luôn tạo 32 ký tự
582
+ const name = manifest.name || 'extension';
583
+ const version = manifest.version || '1.0.0';
584
+ // Dùng SHA256 và lấy 32 ký tự đầu tiên (hex)
585
+ const hash = crypto
586
+ .createHash('sha256')
587
+ .update(`${name}${version}${Date.now()}`)
588
+ .digest('hex')
589
+ .substring(0, 32);
590
+ return hash;
591
+ }
592
+ /**
593
+ * Lấy version từ manifest.json
594
+ */
595
+ static getExtensionVersion(manifest) {
596
+ return manifest.version || '1.0.0';
597
+ }
598
+ /**
599
+ * Copy extension vào profile Extensions directory
600
+ * Structure: {profile}/Default/Extensions/{extension-id}/{version}/
601
+ */
602
+ static async installExtensionToProfile(extensionPath, profilePath, originalExtensionInput) {
603
+ console.log(`[ExtensionHandler] Installing extension to profile: ${extensionPath} -> ${profilePath}`);
604
+ // Đọc manifest
605
+ const manifest = this.readManifest(extensionPath);
606
+ const extensionId = this.getExtensionId(manifest, originalExtensionInput);
607
+ const version = this.getExtensionVersion(manifest);
608
+ // Tạo Extensions directory structure
609
+ const extensionsDir = path.join(profilePath, 'Default', 'Extensions');
610
+ if (!fs.existsSync(extensionsDir)) {
611
+ fs.mkdirSync(extensionsDir, { recursive: true });
612
+ }
613
+ const extensionIdDir = path.join(extensionsDir, extensionId);
614
+ if (!fs.existsSync(extensionIdDir)) {
615
+ fs.mkdirSync(extensionIdDir, { recursive: true });
616
+ }
617
+ // Version directory (format: {version}_0)
618
+ const versionDir = path.join(extensionIdDir, `${version}_0`);
619
+ if (fs.existsSync(versionDir)) {
620
+ // Nếu đã tồn tại, xóa và copy lại
621
+ console.log(`[ExtensionHandler] Removing existing version directory: ${versionDir}`);
622
+ fs.rmSync(versionDir, { recursive: true, force: true });
623
+ }
624
+ fs.mkdirSync(versionDir, { recursive: true });
625
+ console.log(`[ExtensionHandler] Created version directory: ${versionDir}`);
626
+ // Copy tất cả files từ extensionPath vào versionDir
627
+ console.log(`[ExtensionHandler] Copying files from ${extensionPath} to ${versionDir}...`);
628
+ this.copyDirectory(extensionPath, versionDir);
629
+ console.log(`[ExtensionHandler] Files copied successfully`);
630
+ // Tạo _metadata directory và verified_contents.json
631
+ const metadataDir = path.join(versionDir, '_metadata');
632
+ fs.mkdirSync(metadataDir, { recursive: true });
633
+ // Tạo verified_contents.json (simplified version)
634
+ const verifiedContents = this.createVerifiedContents(extensionId, version, versionDir);
635
+ const verifiedContentsPath = path.join(metadataDir, 'verified_contents.json');
636
+ fs.writeFileSync(verifiedContentsPath, JSON.stringify(verifiedContents, null, 2), 'utf-8');
637
+ console.log(`[ExtensionHandler] ✓ Extension installed: ${extensionId} v${version} -> ${versionDir}`);
638
+ return {
639
+ extensionId,
640
+ version,
641
+ path: versionDir,
642
+ manifest,
643
+ };
644
+ }
645
+ /**
646
+ * Copy directory recursively
647
+ */
648
+ static copyDirectory(src, dest) {
649
+ if (!fs.existsSync(src)) {
650
+ throw new Error(`Source directory does not exist: ${src}`);
651
+ }
652
+ const entries = fs.readdirSync(src, { withFileTypes: true });
653
+ for (const entry of entries) {
654
+ const srcPath = path.join(src, entry.name);
655
+ const destPath = path.join(dest, entry.name);
656
+ if (entry.isDirectory()) {
657
+ // Skip _metadata if copying from another profile
658
+ if (entry.name === '_metadata' && fs.existsSync(destPath)) {
659
+ continue;
660
+ }
661
+ fs.mkdirSync(destPath, { recursive: true });
662
+ this.copyDirectory(srcPath, destPath);
663
+ }
664
+ else {
665
+ fs.copyFileSync(srcPath, destPath);
666
+ }
667
+ }
668
+ }
669
+ /**
670
+ * Tạo verified_contents.json structure
671
+ */
672
+ static createVerifiedContents(extensionId, version, extensionPath) {
673
+ // Simplified verified_contents - Chrome sẽ tự verify khi cần
674
+ // Đây là format cơ bản, Chrome có thể tự generate lại khi cần
675
+ return [
676
+ {
677
+ description: 'treehash per file',
678
+ signed_content: {
679
+ payload: '',
680
+ signatures: [],
681
+ },
682
+ },
683
+ ];
684
+ }
685
+ /**
686
+ * Validate extension và lấy thông tin chi tiết
687
+ */
688
+ static async validateAndGetExtensionInfo(extension, workspacePath) {
689
+ const validation = await this.validateExtension(extension, workspacePath);
690
+ if (validation.valid && validation.path) {
691
+ try {
692
+ const manifest = this.readManifest(validation.path);
693
+ const extensionId = this.getExtensionId(manifest, extension);
694
+ const version = this.getExtensionVersion(manifest);
695
+ return {
696
+ ...validation,
697
+ extensionId,
698
+ version,
699
+ };
700
+ }
701
+ catch (error) {
702
+ console.warn(`[ExtensionHandler] Could not read extension info:`, error);
703
+ return validation;
704
+ }
705
+ }
706
+ return validation;
707
+ }
512
708
  /**
513
709
  * Get Chrome arguments để load extensions
514
710
  */
@@ -29,4 +29,13 @@ export declare class ProfileManager {
29
29
  * Đảm bảo file tồn tại và có cấu trúc gologin đầy đủ
30
30
  */
31
31
  private initializePreferencesFile;
32
+ /**
33
+ * Lưu extension configuration vào Preferences file
34
+ */
35
+ saveExtensionsToPreferences(profilePath: string, extensions: Array<{
36
+ extensionId: string;
37
+ version: string;
38
+ path: string;
39
+ manifest?: any;
40
+ }>): void;
32
41
  }
@@ -636,6 +636,21 @@ class ProfileManager {
636
636
  prefs.gologin.webgl_noise_value = "5.669";
637
637
  }
638
638
  }
639
+ // Đảm bảo extensions structure tồn tại ngay từ đầu (để Developer mode được bật)
640
+ if (!prefs.extensions) {
641
+ prefs.extensions = {};
642
+ }
643
+ if (!prefs.extensions.preferences) {
644
+ prefs.extensions.preferences = {};
645
+ }
646
+ if (!prefs.extensions.preferences.extensions) {
647
+ prefs.extensions.preferences.extensions = {};
648
+ }
649
+ if (!prefs.extensions.preferences.extensions.ui) {
650
+ prefs.extensions.preferences.extensions.ui = {};
651
+ }
652
+ // Bật Developer mode ngay từ đầu
653
+ prefs.extensions.preferences.extensions.ui.developer_mode = true;
639
654
  // Ghi file
640
655
  fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
641
656
  }
@@ -643,5 +658,91 @@ class ProfileManager {
643
658
  // Ignore errors - initialization is optional
644
659
  }
645
660
  }
661
+ /**
662
+ * Lưu extension configuration vào Preferences file
663
+ */
664
+ saveExtensionsToPreferences(profilePath, extensions) {
665
+ try {
666
+ const prefsPath = path.join(profilePath, 'Default', 'Preferences');
667
+ let prefs = {};
668
+ if (fs.existsSync(prefsPath)) {
669
+ try {
670
+ const prefsContent = fs.readFileSync(prefsPath, 'utf-8');
671
+ prefs = JSON.parse(prefsContent);
672
+ }
673
+ catch (error) {
674
+ console.error(`[ProfileManager] Error reading Preferences:`, error);
675
+ return;
676
+ }
677
+ }
678
+ else {
679
+ console.warn(`[ProfileManager] Preferences file does not exist, creating new one`);
680
+ }
681
+ // Khởi tạo extensions structure
682
+ if (!prefs.extensions) {
683
+ prefs.extensions = {};
684
+ }
685
+ if (!prefs.extensions.settings) {
686
+ prefs.extensions.settings = {};
687
+ }
688
+ if (!prefs.extensions.preferences) {
689
+ prefs.extensions.preferences = {};
690
+ }
691
+ // Lưu từng extension
692
+ const extensionIds = [];
693
+ for (const ext of extensions) {
694
+ const extensionId = ext.extensionId;
695
+ extensionIds.push(extensionId);
696
+ // Đọc manifest nếu chưa có
697
+ let manifest = ext.manifest;
698
+ if (!manifest) {
699
+ try {
700
+ const manifestPath = path.join(ext.path, 'manifest.json');
701
+ if (fs.existsSync(manifestPath)) {
702
+ const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
703
+ manifest = JSON.parse(manifestContent);
704
+ }
705
+ }
706
+ catch (error) {
707
+ console.warn(`[ProfileManager] Could not read manifest for ${extensionId}:`, error);
708
+ }
709
+ }
710
+ // Tính relative path từ Default directory
711
+ const relativePath = path.relative(path.join(profilePath, 'Default'), ext.path).replace(/\\/g, '/');
712
+ // Cấu hình extension settings
713
+ prefs.extensions.settings[extensionId] = {
714
+ location: 1,
715
+ creation_flags: 1,
716
+ path: relativePath,
717
+ state: 1,
718
+ incognito: 0,
719
+ manifest: manifest || {},
720
+ granted_permissions: manifest?.permissions || [],
721
+ withheld_permissions: [],
722
+ disable_reasons: 0,
723
+ installation_timestamp: Date.now(),
724
+ update_url: '',
725
+ };
726
+ console.log(`[ProfileManager] ✓ Configured extension ${extensionId} in Preferences`);
727
+ }
728
+ // Lưu danh sách extension IDs và bật Developer mode
729
+ prefs.extensions.preferences = {
730
+ ...prefs.extensions.preferences,
731
+ extensions: {
732
+ ...(prefs.extensions.preferences.extensions || {}),
733
+ ui: {
734
+ developer_mode: true, // Bật Developer mode để cho phép cài extension thủ công
735
+ },
736
+ },
737
+ };
738
+ // Lưu lại Preferences
739
+ fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
740
+ console.log(`[ProfileManager] ✓ Saved ${extensionIds.length} extension(s) to Preferences`);
741
+ }
742
+ catch (error) {
743
+ console.error(`[ProfileManager] Error saving extensions to Preferences:`, error);
744
+ throw error;
745
+ }
746
+ }
646
747
  }
647
748
  exports.ProfileManager = ProfileManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-browser",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "description": "n8n nodes for managing Chrome browser profiles and page interactions with Puppeteer automation",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",