vg-coder-cli 2.0.32 → 2.0.34

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 (33) hide show
  1. package/package.json +2 -2
  2. package/.vgignore +0 -10
  3. package/ARCHITECTURE.md +0 -255
  4. package/change.sh +0 -0
  5. package/gulpfile.js +0 -111
  6. package/vetgo-auto/README.md +0 -3
  7. package/vetgo-auto/chrome/CSP_IMPROVEMENTS.md +0 -147
  8. package/vetgo-auto/chrome/MANIFEST_V3_MIGRATION.md +0 -123
  9. package/vetgo-auto/chrome/assets/icon128.png +0 -0
  10. package/vetgo-auto/chrome/assets/icon16.png +0 -0
  11. package/vetgo-auto/chrome/assets/icon48.png +0 -0
  12. package/vetgo-auto/chrome/environments/environment.ts +0 -13
  13. package/vetgo-auto/chrome/manifest.json +0 -66
  14. package/vetgo-auto/chrome/rules.json +0 -23
  15. package/vetgo-auto/chrome/src/background.ts +0 -200
  16. package/vetgo-auto/chrome/src/controller.ts +0 -172
  17. package/vetgo-auto/chrome/src/controllers/common.firebase.ts +0 -31
  18. package/vetgo-auto/chrome/src/controllers/firebase-crud.ts +0 -147
  19. package/vetgo-auto/chrome/src/controllers/load-common-fuc.controller.ts +0 -24
  20. package/vetgo-auto/chrome/src/controllers/load-script.controller.ts +0 -23
  21. package/vetgo-auto/chrome/src/script-injector.ts +0 -305
  22. package/vetgo-auto/chrome/src/sidepanel.css +0 -166
  23. package/vetgo-auto/chrome/src/sidepanel.html +0 -48
  24. package/vetgo-auto/chrome/src/sidepanel.ts +0 -127
  25. package/vetgo-auto/chrome/src/utils/ai-domains.ts +0 -33
  26. package/vetgo-auto/chrome/src/utils/db-utils.ts +0 -2
  27. package/vetgo-auto/chrome/src/utils/environment-storage.service.ts +0 -85
  28. package/vetgo-auto/chrome/src/utils/injector-script.ts +0 -47
  29. package/vetgo-auto/chrome/webpack.config.js +0 -53
  30. package/vetgo-auto/chrome/webpack.config.prod.js +0 -54
  31. package/vetgo-auto/package.json +0 -30
  32. package/vetgo-auto/tsconfig.json +0 -27
  33. package/vetgo-auto/vg-coder.zip +0 -0
@@ -1,305 +0,0 @@
1
- // Alternative script injection methods for CSP-restricted environments
2
-
3
- export class ScriptInjector {
4
-
5
- /**
6
- * Detect if site has strict CSP that blocks blob/data URLs
7
- */
8
- private static detectStrictCSP(): boolean {
9
- try {
10
- // Check for CSP meta tags
11
- const cspMetas = document.querySelectorAll('meta[http-equiv="Content-Security-Policy"]');
12
- for (let i = 0; i < cspMetas.length; i++) {
13
- const meta = cspMetas[i];
14
- const content = meta.getAttribute('content') || '';
15
- if (content.includes('script-src') && !content.includes('blob:') && !content.includes('data:')) {
16
- return true;
17
- }
18
- }
19
-
20
- // Check common strict CSP domains
21
- const strictDomains = [
22
- 'midjourney.com',
23
- 'openai.com',
24
- 'github.com',
25
- 'google.com',
26
- 'googletagmanager.com',
27
- 'facebook.com',
28
- 'twitter.com',
29
- 'linkedin.com'
30
- ];
31
-
32
- const hostname = window.location.hostname.toLowerCase();
33
- return strictDomains.some(domain => hostname.includes(domain));
34
- } catch (error) {
35
- console.error('CSP detection error:', error);
36
- return false;
37
- }
38
- }
39
-
40
- /**
41
- * Safely get head element or wait for it
42
- */
43
- private static getHeadElement(): Promise<HTMLHeadElement> {
44
- return new Promise((resolve) => {
45
- const tryGetHead = () => {
46
- const head = document.head || document.getElementsByTagName('head')[0];
47
- if (head) {
48
- resolve(head as HTMLHeadElement);
49
- return;
50
- }
51
-
52
- // If no head, wait for DOM
53
- if (document.readyState === 'loading') {
54
- document.addEventListener('DOMContentLoaded', tryGetHead, { once: true });
55
- } else {
56
- // Create head if it doesn't exist
57
- const newHead = document.createElement('head');
58
- if (document.documentElement) {
59
- document.documentElement.appendChild(newHead);
60
- resolve(newHead);
61
- } else {
62
- // Last resort: wait a bit and try again
63
- setTimeout(tryGetHead, 100);
64
- }
65
- }
66
- };
67
-
68
- tryGetHead();
69
- });
70
- }
71
-
72
- /**
73
- * Method 1: Blob URL injection (works in most cases)
74
- */
75
- static async injectViaBlob(script: string, actionType: string): Promise<boolean> {
76
- try {
77
- const blob = new Blob([script], { type: 'application/javascript' });
78
- const blobUrl = URL.createObjectURL(blob);
79
-
80
- const scriptElement = document.createElement('script');
81
- scriptElement.type = 'text/javascript';
82
- scriptElement.id = actionType;
83
- scriptElement.src = blobUrl;
84
-
85
- return new Promise(async (resolve) => {
86
- scriptElement.onload = () => {
87
- URL.revokeObjectURL(blobUrl);
88
- resolve(true);
89
- };
90
-
91
- scriptElement.onerror = () => {
92
- URL.revokeObjectURL(blobUrl);
93
- resolve(false);
94
- };
95
-
96
- try {
97
- const head = await this.getHeadElement();
98
- head.appendChild(scriptElement);
99
- } catch (error) {
100
- console.error('Failed to get head element:', error);
101
- URL.revokeObjectURL(blobUrl);
102
- resolve(false);
103
- }
104
- });
105
- } catch (error) {
106
- console.error('Blob injection failed:', error);
107
- return false;
108
- }
109
- }
110
-
111
- /**
112
- * Method 2: Data URL injection (fallback)
113
- */
114
- static async injectViaDataUrl(script: string, actionType: string): Promise<boolean> {
115
- try {
116
- const dataUrl = 'data:application/javascript;base64,' + btoa(script);
117
- const scriptElement = document.createElement('script');
118
- scriptElement.type = 'text/javascript';
119
- scriptElement.id = actionType + '_data';
120
- scriptElement.src = dataUrl;
121
-
122
- return new Promise(async (resolve) => {
123
- scriptElement.onload = () => resolve(true);
124
- scriptElement.onerror = () => resolve(false);
125
-
126
- try {
127
- const head = await this.getHeadElement();
128
- head.appendChild(scriptElement);
129
- } catch (error) {
130
- console.error('Failed to get head element for data URL:', error);
131
- resolve(false);
132
- }
133
- });
134
- } catch (error) {
135
- console.error('Data URL injection failed:', error);
136
- return false;
137
- }
138
- }
139
-
140
- /**
141
- * Method 3: PostMessage bridge injection
142
- */
143
- static injectViaPostMessage(script: string, actionType: string): Promise<boolean> {
144
- return new Promise((resolve) => {
145
- try {
146
- // Create a bridge script that listens for postMessage
147
- const bridgeScript = `
148
- window.addEventListener('message', function(event) {
149
- if (event.source !== window || !event.data.type || event.data.type !== 'VETGO_SCRIPT_INJECT') {
150
- return;
151
- }
152
- try {
153
- const func = new Function(event.data.script);
154
- func();
155
- window.postMessage({type: 'VETGO_SCRIPT_SUCCESS', actionType: event.data.actionType}, '*');
156
- } catch (error) {
157
- console.error('PostMessage script execution failed:', error);
158
- window.postMessage({type: 'VETGO_SCRIPT_ERROR', actionType: event.data.actionType, error: error.message}, '*');
159
- }
160
- });
161
- `;
162
-
163
- // First inject the bridge
164
- this.injectViaBlob(bridgeScript, actionType + '_bridge').then((bridgeSuccess) => {
165
- if (bridgeSuccess) {
166
- // Listen for response
167
- const responseHandler = (event: MessageEvent) => {
168
- if (event.source !== window || !event.data.type) return;
169
-
170
- if (event.data.type === 'VETGO_SCRIPT_SUCCESS' && event.data.actionType === actionType) {
171
- window.removeEventListener('message', responseHandler);
172
- resolve(true);
173
- } else if (event.data.type === 'VETGO_SCRIPT_ERROR' && event.data.actionType === actionType) {
174
- window.removeEventListener('message', responseHandler);
175
- resolve(false);
176
- }
177
- };
178
-
179
- window.addEventListener('message', responseHandler);
180
-
181
- // Send script via postMessage
182
- window.postMessage({
183
- type: 'VETGO_SCRIPT_INJECT',
184
- script: script,
185
- actionType: actionType
186
- }, '*');
187
-
188
- // Timeout after 5 seconds
189
- setTimeout(() => {
190
- window.removeEventListener('message', responseHandler);
191
- resolve(false);
192
- }, 5000);
193
- } else {
194
- resolve(false);
195
- }
196
- });
197
- } catch (error) {
198
- console.error('PostMessage injection failed:', error);
199
- resolve(false);
200
- }
201
- });
202
- }
203
-
204
- /**
205
- * Method 4: Background script injection via chrome.scripting
206
- */
207
- static injectViaBackground(script: string, actionType: string): Promise<boolean> {
208
- return new Promise((resolve) => {
209
- try {
210
- chrome.runtime.sendMessage({
211
- action: "INJECT_SCRIPT",
212
- script: script,
213
- actionType: actionType
214
- }, (response) => {
215
- if (chrome.runtime.lastError) {
216
- console.error('Background injection failed:', chrome.runtime.lastError);
217
- resolve(false);
218
- } else {
219
- resolve(response && response.success);
220
- }
221
- });
222
- } catch (error) {
223
- console.error('Background script injection failed:', error);
224
- resolve(false);
225
- }
226
- });
227
- }
228
-
229
- /**
230
- * Method 5: Direct eval in content script context (last resort)
231
- */
232
- static injectViaEval(script: string, actionType: string): Promise<boolean> {
233
- return new Promise((resolve) => {
234
- try {
235
- console.log(`Attempting direct eval for ${actionType}`);
236
-
237
- // Create isolated function to avoid polluting global scope
238
- const isolatedExecution = new Function('script', `
239
- try {
240
- ${script}
241
- return true;
242
- } catch (error) {
243
- console.error('Eval execution error:', error);
244
- return false;
245
- }
246
- `);
247
-
248
- const success = isolatedExecution(script);
249
- resolve(success);
250
- } catch (error) {
251
- console.error('Eval injection failed:', error);
252
- resolve(false);
253
- }
254
- });
255
- }
256
-
257
- /**
258
- * Try all methods in smart order based on CSP detection
259
- */
260
- static async injectScript(script: string, actionType: string): Promise<boolean> {
261
- const hasStrictCSP = this.detectStrictCSP();
262
-
263
- // Smart method ordering based on CSP detection
264
- const methods = hasStrictCSP ? [
265
- // For strict CSP sites, try background injection first
266
- () => this.injectViaBackground(script, actionType),
267
- () => this.injectViaEval(script, actionType),
268
- () => this.injectViaPostMessage(script, actionType),
269
- () => this.injectViaBlob(script, actionType),
270
- () => this.injectViaDataUrl(script, actionType)
271
- ] : [
272
- // For normal sites, try blob first (fastest)
273
- () => this.injectViaBlob(script, actionType),
274
- () => this.injectViaDataUrl(script, actionType),
275
- () => this.injectViaBackground(script, actionType),
276
- () => this.injectViaEval(script, actionType),
277
- () => this.injectViaPostMessage(script, actionType)
278
- ];
279
-
280
- console.log(`Injecting script for ${actionType} (Strict CSP: ${hasStrictCSP})`);
281
-
282
- for (let i = 0; i < methods.length; i++) {
283
- const method = methods[i];
284
- const methodName = hasStrictCSP ?
285
- ['Background', 'Eval', 'PostMessage', 'Blob', 'DataURL'][i] :
286
- ['Blob', 'DataURL', 'Background', 'Eval', 'PostMessage'][i];
287
-
288
- try {
289
- console.log(`Trying ${methodName} injection...`);
290
- const success = await method();
291
- if (success) {
292
- console.log(`✅ Script injection successful via ${methodName} for ${actionType}`);
293
- return true;
294
- } else {
295
- console.log(`❌ ${methodName} injection failed for ${actionType}`);
296
- }
297
- } catch (error) {
298
- console.error(`❌ ${methodName} injection error:`, error);
299
- }
300
- }
301
-
302
- console.error(`❌ All injection methods failed for ${actionType}`);
303
- return false;
304
- }
305
- }
@@ -1,166 +0,0 @@
1
- * {
2
- margin: 0;
3
- padding: 0;
4
- box-sizing: border-box;
5
- }
6
-
7
- body {
8
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
9
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
10
- min-height: 100vh;
11
- display: flex;
12
- align-items: flex-start;
13
- justify-content: center;
14
- padding: 0;
15
- }
16
-
17
- .container {
18
- background: white;
19
- border-radius: 0;
20
- box-shadow: none;
21
- padding: 24px;
22
- width: 100%;
23
- min-height: 100vh;
24
- }
25
-
26
- h1 {
27
- color: #333;
28
- font-size: 24px;
29
- margin-bottom: 8px;
30
- font-weight: 600;
31
- }
32
-
33
- .subtitle {
34
- color: #666;
35
- font-size: 13px;
36
- margin-bottom: 24px;
37
- }
38
-
39
- .form-group {
40
- margin-bottom: 20px;
41
- }
42
-
43
- label {
44
- display: block;
45
- color: #444;
46
- font-size: 13px;
47
- font-weight: 500;
48
- margin-bottom: 6px;
49
- }
50
-
51
- input[type="text"] {
52
- width: 100%;
53
- padding: 10px 12px;
54
- border: 2px solid #e0e0e0;
55
- border-radius: 6px;
56
- font-size: 14px;
57
- transition: all 0.3s ease;
58
- outline: none;
59
- }
60
-
61
- input[type="text"]:focus {
62
- border-color: #667eea;
63
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
64
- }
65
-
66
- .hint {
67
- color: #888;
68
- font-size: 11px;
69
- margin-top: 4px;
70
- font-style: italic;
71
- }
72
-
73
- .button-group {
74
- display: flex;
75
- gap: 10px;
76
- margin-top: 24px;
77
- }
78
-
79
- button {
80
- flex: 1;
81
- padding: 10px 20px;
82
- border: none;
83
- border-radius: 6px;
84
- font-size: 14px;
85
- font-weight: 600;
86
- cursor: pointer;
87
- transition: all 0.3s ease;
88
- outline: none;
89
- }
90
-
91
- .btn-primary {
92
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
93
- color: white;
94
- }
95
-
96
- .btn-primary:hover {
97
- transform: translateY(-1px);
98
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
99
- }
100
-
101
- .btn-primary:active {
102
- transform: translateY(0);
103
- }
104
-
105
- .btn-secondary {
106
- background: #f5f5f5;
107
- color: #666;
108
- }
109
-
110
- .btn-secondary:hover {
111
- background: #e0e0e0;
112
- }
113
-
114
- .message {
115
- padding: 10px 14px;
116
- border-radius: 6px;
117
- margin-bottom: 16px;
118
- font-size: 13px;
119
- display: none;
120
- animation: slideIn 0.3s ease;
121
- }
122
-
123
- @keyframes slideIn {
124
- from {
125
- opacity: 0;
126
- transform: translateY(-10px);
127
- }
128
- to {
129
- opacity: 1;
130
- transform: translateY(0);
131
- }
132
- }
133
-
134
- .message.success {
135
- background: #d4edda;
136
- color: #155724;
137
- border: 1px solid #c3e6cb;
138
- display: block;
139
- }
140
-
141
- .message.error {
142
- background: #f8d7da;
143
- color: #721c24;
144
- border: 1px solid #f5c6cb;
145
- display: block;
146
- }
147
-
148
- .current-value {
149
- background: #f8f9fa;
150
- padding: 10px 14px;
151
- border-radius: 6px;
152
- margin-bottom: 16px;
153
- border-left: 4px solid #667eea;
154
- }
155
-
156
- .current-value strong {
157
- color: #667eea;
158
- font-weight: 600;
159
- font-size: 13px;
160
- }
161
-
162
- .current-value span {
163
- color: #333;
164
- font-family: 'Courier New', monospace;
165
- font-size: 13px;
166
- }
@@ -1,48 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="vi">
3
-
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>VetGo Pro - Cấu hình</title>
8
- <link rel="stylesheet" href="sidepanel.css">
9
- </head>
10
-
11
- <body>
12
- <div class="container">
13
- <h1>⚙️ Cấu hình VetGo Pro</h1>
14
- <p class="subtitle">Quản lý cài đặt môi trường cho extension</p>
15
-
16
- <div id="message" class="message"></div>
17
-
18
- <div class="current-value">
19
- <strong>Môi trường hiện tại:</strong> <span id="currentEnv">Đang tải...</span>
20
- </div>
21
-
22
- <form id="configForm">
23
- <div class="form-group">
24
- <label for="environmentName">Tên môi trường (Environment Name)</label>
25
- <input type="text" id="environmentName" name="environmentName"
26
- placeholder="Nhập tên môi trường (ví dụ: midjourney, production, test)" required />
27
- <p class="hint">Tên môi trường sẽ được sử dụng để load scripts từ Firebase: ENV/{environmentName}/script</p>
28
- </div>
29
-
30
- <div class="form-group">
31
- <label for="firebaseConfig">Cấu hình Firebase (JSON)</label>
32
- <textarea id="firebaseConfig" name="firebaseConfig" rows="8"
33
- placeholder='{"apiKey": "...", "authDomain": "...", "databaseURL": "..."}'
34
- style="width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-family: monospace; font-size: 13px;"></textarea>
35
- <p class="hint">Để trống để sử dụng cấu hình mặc định của hệ thống. Nhập JSON object chứa apiKey, authDomain, databaseURL...</p>
36
- </div>
37
-
38
- <div class="button-group">
39
- <button type="submit" class="btn-primary">💾 Lưu cấu hình</button>
40
- <button type="button" id="resetBtn" class="btn-secondary">🔄 Đặt lại mặc định</button>
41
- </div>
42
- </form>
43
- </div>
44
-
45
- <script src="sidepanel.js"></script>
46
- </body>
47
-
48
- </html>
@@ -1,127 +0,0 @@
1
- import { EnvironmentStorageService } from './utils/environment-storage.service';
2
- import { environment } from '../environments/environment';
3
-
4
- // DOM Elements
5
- const form = document.getElementById('configForm') as HTMLFormElement;
6
- const environmentInput = document.getElementById('environmentName') as HTMLInputElement;
7
- const firebaseConfigInput = document.getElementById('firebaseConfig') as HTMLTextAreaElement;
8
- const resetBtn = document.getElementById('resetBtn') as HTMLButtonElement;
9
- const messageDiv = document.getElementById('message') as HTMLDivElement;
10
- const currentEnvSpan = document.getElementById('currentEnv') as HTMLSpanElement;
11
-
12
- /**
13
- * Hiển thị thông báo cho người dùng
14
- */
15
- function showMessage(text: string, type: 'success' | 'error') {
16
- messageDiv.textContent = text;
17
- messageDiv.className = `message ${type}`;
18
-
19
- // Tự động ẩn thông báo sau 3 giây
20
- setTimeout(() => {
21
- messageDiv.className = 'message';
22
- }, 3000);
23
- }
24
-
25
- /**
26
- * Load và hiển thị giá trị hiện tại
27
- */
28
- async function loadCurrentConfig() {
29
- try {
30
- // Load Env Name
31
- const envName = await EnvironmentStorageService.getEnvironmentName();
32
- currentEnvSpan.textContent = envName;
33
- environmentInput.value = envName;
34
-
35
- // Load Firebase Config
36
- const fbConfig = await EnvironmentStorageService.getFirebaseConfig();
37
-
38
- // Kiểm tra xem có phải config mặc định không
39
- const isDefault = JSON.stringify(fbConfig) === JSON.stringify(environment.firebaseConfig);
40
-
41
- if (!isDefault) {
42
- firebaseConfigInput.value = JSON.stringify(fbConfig, null, 2);
43
- } else {
44
- firebaseConfigInput.value = ''; // Để trống nếu đang dùng default
45
- firebaseConfigInput.placeholder = `Đang sử dụng mặc định:\n${JSON.stringify(environment.firebaseConfig, null, 2)}`;
46
- }
47
-
48
- } catch (error) {
49
- console.error('Error loading config:', error);
50
- currentEnvSpan.textContent = 'Lỗi khi tải cấu hình';
51
- showMessage('Không thể tải cấu hình hiện tại', 'error');
52
- }
53
- }
54
-
55
- /**
56
- * Xử lý sự kiện submit form
57
- */
58
- async function handleSubmit(event: Event) {
59
- event.preventDefault();
60
-
61
- const newEnvName = environmentInput.value.trim();
62
- const firebaseConfigStr = firebaseConfigInput.value.trim();
63
-
64
- if (!newEnvName) {
65
- showMessage('Vui lòng nhập tên môi trường', 'error');
66
- return;
67
- }
68
-
69
- try {
70
- // 1. Lưu Environment Name
71
- await EnvironmentStorageService.setEnvironmentName(newEnvName);
72
- currentEnvSpan.textContent = newEnvName;
73
-
74
- // 2. Lưu Firebase Config
75
- if (firebaseConfigStr) {
76
- try {
77
- const configObj = JSON.parse(firebaseConfigStr);
78
- // Validate sơ bộ
79
- if (!configObj.apiKey || !configObj.databaseURL) {
80
- throw new Error("Config thiếu apiKey hoặc databaseURL");
81
- }
82
- await EnvironmentStorageService.setFirebaseConfig(configObj);
83
- } catch (e) {
84
- showMessage('❌ JSON Firebase Config không hợp lệ: ' + (e as Error).message, 'error');
85
- return;
86
- }
87
- } else {
88
- // Nếu để trống, xóa custom config để dùng default
89
- await new Promise<void>((resolve) => {
90
- chrome.storage.sync.remove('firebaseConfig', () => resolve());
91
- });
92
- }
93
-
94
- showMessage('✅ Lưu cấu hình thành công!', 'success');
95
- // Reload lại hiển thị để update placeholder/value
96
- loadCurrentConfig();
97
-
98
- } catch (error) {
99
- console.error('Error saving settings:', error);
100
- showMessage('❌ Lỗi khi lưu cấu hình: ' + (error as Error).message, 'error');
101
- }
102
- }
103
-
104
- /**
105
- * Xử lý sự kiện reset về mặc định
106
- */
107
- async function handleReset() {
108
- if (!confirm('Bạn có chắc muốn đặt lại tất cả về mặc định?')) {
109
- return;
110
- }
111
-
112
- try {
113
- await EnvironmentStorageService.resetToDefault();
114
- await loadCurrentConfig(); // Reload UI
115
- showMessage('✅ Đã đặt lại về cấu hình mặc định!', 'success');
116
- } catch (error) {
117
- console.error('Error resetting environment:', error);
118
- showMessage('❌ Lỗi khi đặt lại cấu hình', 'error');
119
- }
120
- }
121
-
122
- // Event Listeners
123
- form.addEventListener('submit', handleSubmit);
124
- resetBtn.addEventListener('click', handleReset);
125
-
126
- // Load giá trị hiện tại khi trang được mở
127
- loadCurrentConfig();
@@ -1,33 +0,0 @@
1
- // AI Chat Providers Configuration
2
- // Domains for automatic VG Coder iframe injection
3
-
4
- export const AI_DOMAINS = [
5
- 'chat.openai.com', // ChatGPT (old URL)
6
- 'chatgpt.com', // ChatGPT (new URL)
7
- 'gemini.google.com', // Google Gemini
8
- 'aistudio.google.com', // Google AI Studio
9
- 'chat.deepseek.com', // DeepSeek
10
- 'kimi.com', // Kimi AI
11
- 'www.kimi.com', // Kimi AI (www)
12
- 'grok.com', // Grok
13
- 'claude.ai', // Claude (Anthropic)
14
- 'poe.com', // Poe
15
- 'perplexity.ai', // Perplexity
16
- 'www.perplexity.ai', // Perplexity (www)
17
- ];
18
-
19
- /**
20
- * Check if domain should have VG Coder iframe injected
21
- */
22
- export function isAIChatDomain(hostname: string): boolean {
23
- const cleanHostname = hostname.toLowerCase().replace(/^www\./, '');
24
- console.log('🔍 Checking AI domain:', cleanHostname);
25
-
26
- const isMatch = AI_DOMAINS.some(domain => {
27
- const cleanDomain = domain.toLowerCase().replace(/^www\./, '');
28
- return cleanHostname === cleanDomain || cleanHostname.endsWith('.' + cleanDomain);
29
- });
30
-
31
- console.log('🎯 AI domain match:', isMatch);
32
- return isMatch;
33
- }
@@ -1,2 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
- export const uuid = (): string => uuidv4();