devicely 2.2.12 → 2.2.13

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 (63) hide show
  1. package/README.md +182 -81
  2. package/bin/devicely.js +1 -1
  3. package/config/devices.conf +2 -2
  4. package/lib/.logging-backup/aiProviders.js.backup +654 -0
  5. package/lib/.logging-backup/appMappings.js.backup +337 -0
  6. package/lib/.logging-backup/commanderService.js.backup +4427 -0
  7. package/lib/.logging-backup/devices.js.backup +54 -0
  8. package/lib/.logging-backup/doctor.js.backup +94 -0
  9. package/lib/.logging-backup/encryption.js.backup +61 -0
  10. package/lib/.logging-backup/executor.js.backup +104 -0
  11. package/lib/.logging-backup/hybridAI.js.backup +154 -0
  12. package/lib/.logging-backup/intelligentLocatorService.js.backup +1541 -0
  13. package/lib/.logging-backup/locatorStrategy.js.backup +342 -0
  14. package/lib/.logging-backup/scriptLoader.js.backup +13 -0
  15. package/lib/.logging-backup/server.js.backup +6298 -0
  16. package/lib/.logging-backup/tensorflowAI.js.backup +714 -0
  17. package/lib/.logging-backup/universalSessionManager.js.backup +370 -0
  18. package/lib/.logging-enhanced-backup/server.js.enhanced-backup +6298 -0
  19. package/lib/advanced-logger.js +1 -0
  20. package/lib/aiProviders.js +154 -15
  21. package/lib/aiProviders.js.strategic-backup +657 -0
  22. package/lib/aiProvidersConfig.js +61 -151
  23. package/lib/aiProvidersConfig.js.backup +218 -0
  24. package/lib/androidDeviceDetection.js +1 -1
  25. package/lib/appMappings.js +1 -1
  26. package/lib/commanderService.js +1 -1
  27. package/lib/commanderService.js.backup +5552 -0
  28. package/lib/deviceDetection.js +1 -1
  29. package/lib/devices.js +1 -1
  30. package/lib/devices.js.strategic-backup +57 -0
  31. package/lib/doctor.js +1 -1
  32. package/lib/encryption.js +1 -1
  33. package/lib/encryption.js.strategic-backup +61 -0
  34. package/lib/executor.js +1 -1
  35. package/lib/executor.js.strategic-backup +107 -0
  36. package/lib/frontend/asset-manifest.json +5 -3
  37. package/lib/frontend/index.html +1 -1
  38. package/lib/hybridAI.js +1 -0
  39. package/lib/intelligentLocatorService.js +1 -0
  40. package/lib/lightweightAI.js +1 -0
  41. package/lib/localBuiltInAI.js +1 -0
  42. package/lib/localBuiltInAI_backup.js +1 -0
  43. package/lib/localBuiltInAI_simple.js +1 -0
  44. package/lib/locatorStrategy.js +1 -1
  45. package/lib/logger-demo.js +2 -0
  46. package/lib/logger-integration-examples.js +102 -0
  47. package/lib/logger.js +1 -1
  48. package/lib/package.json +5 -0
  49. package/lib/public/asset-manifest.json +3 -3
  50. package/lib/public/index.html +1 -1
  51. package/lib/quick-start-logger.js +2 -0
  52. package/lib/scriptLoader.js +1 -1
  53. package/lib/server.js +1 -1
  54. package/lib/server.js.strategic-backup +6298 -0
  55. package/lib/tensorflowAI.js +1 -0
  56. package/lib/tensorflowAI.js.strategic-backup +717 -0
  57. package/lib/tinyAI.js +1 -0
  58. package/lib/universalSessionManager.js +1 -0
  59. package/package.json +1 -1
  60. package/scripts/shell/android_device_control.enc +1 -1
  61. package/scripts/shell/connect_ios_usb_multi_final.enc +1 -1
  62. package/scripts/shell/connect_ios_wireless_multi_final.enc +1 -1
  63. package/lib/public/index.html.bak +0 -1
@@ -0,0 +1,654 @@
1
+ // AI Provider Abstraction Layer - Enhanced Multi-Provider Support
2
+ // Supports: OpenAI, Google Gemini, Anthropic Claude, GitHub Copilot, Groq, Cohere, Mistral AI, Local Built-In AI
3
+
4
+ const { OpenAI } = require('openai');
5
+ const { GoogleGenerativeAI } = require('@google/generative-ai');
6
+ const { Anthropic } = require('@anthropic-ai/sdk');
7
+ const { getPackageId, APP_MAPPINGS } = require('./appMappings');
8
+ const LocalBuiltInAI = require('./localBuiltInAI'); // NEW: Import local AI
9
+
10
+ class AIProviderManager {
11
+ constructor() {
12
+ this.provider = process.env.AI_PROVIDER || 'tensorflow'; // Default to TensorFlow.js AI
13
+ this.model = process.env.AI_MODEL || null; // Auto-select default if null
14
+
15
+ // Initialize Local Built-In AI first
16
+ this.localAI = new LocalBuiltInAI();
17
+
18
+ this.initializeProviders();
19
+ }
20
+
21
+ initializeProviders() {
22
+ // OpenAI
23
+ if (process.env.OPENAI_API_KEY) {
24
+ this.openai = new OpenAI({
25
+ apiKey: process.env.OPENAI_API_KEY,
26
+ });
27
+ }
28
+
29
+ // Google Gemini
30
+ if (process.env.GEMINI_API_KEY) {
31
+ this.gemini = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
32
+ }
33
+
34
+ // Anthropic Claude
35
+ if (process.env.CLAUDE_API_KEY) {
36
+ this.claude = new Anthropic({
37
+ apiKey: process.env.CLAUDE_API_KEY,
38
+ });
39
+ }
40
+
41
+ // GitHub Copilot
42
+ if (process.env.GITHUB_TOKEN) {
43
+ this.copilot = new OpenAI({
44
+ apiKey: process.env.GITHUB_TOKEN,
45
+ baseURL: 'https://api.githubcopilot.com',
46
+ });
47
+ }
48
+
49
+ // Groq (ultra-fast inference)
50
+ if (process.env.GROQ_API_KEY) {
51
+ this.groq = new OpenAI({
52
+ apiKey: process.env.GROQ_API_KEY,
53
+ baseURL: 'https://api.groq.com/openai/v1',
54
+ });
55
+ }
56
+
57
+ // Cohere
58
+ if (process.env.COHERE_API_KEY) {
59
+ this.cohere = new OpenAI({
60
+ apiKey: process.env.COHERE_API_KEY,
61
+ baseURL: 'https://api.cohere.ai/v1',
62
+ });
63
+ }
64
+
65
+ // Mistral AI
66
+ if (process.env.MISTRAL_API_KEY) {
67
+ this.mistral = new OpenAI({
68
+ apiKey: process.env.MISTRAL_API_KEY,
69
+ baseURL: 'https://api.mistral.ai/v1',
70
+ });
71
+ }
72
+ }
73
+
74
+ getSystemPrompt(platform = null) {
75
+ // Generate app list based on platform
76
+ let appListSection = '';
77
+
78
+ if (platform === 'both') {
79
+ // Multi-platform mode - use generic app names
80
+ const commonApps = Object.keys(APP_MAPPINGS)
81
+ .filter(app => APP_MAPPINGS[app].ios && APP_MAPPINGS[app].android)
82
+ .slice(0, 30);
83
+
84
+ appListSection = `\n\nMULTI-PLATFORM MODE (iOS + Android devices)
85
+ Available apps: ${commonApps.join(', ')}
86
+
87
+ IMPORTANT: Use generic app names (e.g., "launch settings", "launch chrome")
88
+ The system will automatically convert to platform-specific package IDs:
89
+ ${commonApps.slice(0, 15).map(app => `- ${app} -> iOS: ${APP_MAPPINGS[app].ios} / Android: ${APP_MAPPINGS[app].android}`).join('\n')}
90
+
91
+ Commands will execute SIMULTANEOUSLY on all devices with correct package IDs.
92
+ `;
93
+ } else if (platform) {
94
+ const availableApps = Object.keys(APP_MAPPINGS)
95
+ .filter(app => APP_MAPPINGS[app][platform])
96
+ .slice(0, 50);
97
+
98
+ appListSection = `\n\nPLATFORM: ${platform.toUpperCase()}
99
+ Available apps for ${platform}: ${availableApps.join(', ')}
100
+
101
+ APP PACKAGE MAPPINGS:
102
+ When user says "launch chrome", "open chrome", etc., use the correct package ID:
103
+ ${availableApps.slice(0, 20).map(app => `- ${app} -> launch ${APP_MAPPINGS[app][platform]}`).join('\n')}
104
+ `;
105
+ }
106
+
107
+ return `You are a COMPLETE mobile automation AI. Convert ENTIRE natural language requests to executable commands.
108
+
109
+ 🚨 CRITICAL: PROCESS THE COMPLETE INPUT - DON'T STOP EARLY! 🚨
110
+
111
+ Available commands (work on both iOS & Android):
112
+ - launch <app_name>: Launch an app using generic name (e.g., "launch settings", "launch chrome")
113
+ - kill <app_name>: Close/force stop an app
114
+ - home: Go to home screen (press home button)
115
+ - back: Navigate back (Android/iOS)
116
+ - url <url>: Open URL in browser
117
+ - click <text>: Click on a button or element by visible text
118
+ - click <x,y>: Click at specific coordinates (e.g., click 500,1000)
119
+ - tap <text/coords>: Same as click
120
+ - longpress <text/coords>: Long press on element or coordinates
121
+ - swipe <direction>: Swipe up/down/left/right (use for scrolling)
122
+ - type <text>: Type text into focused field (just the text, no "type" prefix)
123
+ - screenshot: Take screenshot
124
+ - restart: Restart device
125
+ - rotate <left/right/portrait/landscape>: Rotate screen
126
+
127
+ iOS-specific commands:
128
+ - darkmode/lightmode: Change appearance
129
+ - airplane <on/off>: Toggle airplane mode
130
+ - wifi <on/off>: Toggle WiFi
131
+ - volume <up/down/mute>: Control volume
132
+
133
+ Android-specific commands:
134
+ - getLocators: Get all interactive elements on current screen
135
+ - recent: Open recent apps
136
+ - notifications: Open notification panel
137
+ - quicksettings: Open quick settings
138
+ ${appListSection}
139
+
140
+ // 🎯 ENHANCED PHRASE MAPPINGS FOR COMPLEX SENTENCES:
141
+ - "launch X and do Y" -> launch X, WAIT, then Y commands
142
+ - "open X then scroll up" -> launch X, WAIT, swipe up
143
+ - "go to X and search Y" -> url X, WAIT, click search, WAIT, type Y
144
+ - "take photo" -> launch camera, WAIT, tap "capture button"
145
+ - "go back and open Y" -> back, WAIT, launch Y
146
+ - "scroll up and tap X" -> swipe up, WAIT, click X
147
+
148
+ // 🎯 COMPLEX SENTENCE EXAMPLES:
149
+ "launch camera and take photo" ->
150
+ launch camera
151
+ WAIT 3000
152
+ screenshot
153
+
154
+ "open settings then scroll up and go back" ->
155
+ launch settings
156
+ WAIT 2000
157
+ swipe up
158
+ WAIT 1000
159
+ back
160
+
161
+ "go to youtube.com search for cats and take screenshot" ->
162
+ url https://www.youtube.com
163
+ WAIT 3000
164
+ click "search"
165
+ WAIT 1000
166
+ type "cats"
167
+ WAIT 1000
168
+ screenshot
169
+
170
+ "launch chrome go to google.com type hello press home" ->
171
+ launch chrome
172
+ WAIT 3000
173
+ url https://www.google.com
174
+ WAIT 2000
175
+ click "search"
176
+ WAIT 1000
177
+ type "hello"
178
+ WAIT 1000
179
+ home
180
+
181
+ Convert this COMPLETE request to commands:
182
+
183
+ // 🔍 🔍 COMMON PHRASE MAPPINGS:
184
+ - "scroll up" OR "scroll down" -> swipe up OR swipe down
185
+ - "go to <url>" OR "open <url>" OR "visit <url>" -> url https://<url>
186
+ - "press home" OR "go home" OR "home button" -> home
187
+ - "open settings" OR "launch settings" -> launch settings
188
+ - "open camera" OR "launch camera" -> launch camera
189
+
190
+ Examples:
191
+ - "open chrome" -> launch chrome
192
+ - "launch settings" -> launch settings
193
+ - "open camera" -> launch camera
194
+ - "scroll up" -> swipe up
195
+ - "scroll down" -> swipe down
196
+ - "click on the login button" -> click Login
197
+ - "tap at center of screen" -> click 540,1000
198
+ - "swipe down" -> swipe down
199
+ - "type hello world" -> hello world
200
+ - "take a screenshot" -> screenshot
201
+ - "go back" -> back
202
+ - "press home" -> home
203
+ - "go to google.com" -> url https://www.google.com
204
+ - "visit youtube.com" -> url https://www.youtube.com
205
+ - "launch Chrome and search google.com" -> launch chrome
206
+ WAIT 3000
207
+ url https://www.google.com
208
+ - "launch settings scroll up launch camera go to google.com press home" ->
209
+ launch settings
210
+ WAIT 3000
211
+ swipe up
212
+ WAIT 1000
213
+ home
214
+ WAIT 500
215
+ launch camera
216
+ WAIT 3000
217
+ home
218
+ WAIT 500
219
+ url https://www.google.com
220
+ WAIT 2000
221
+ home
222
+
223
+ Convert this request to commands: "{INPUT}"
224
+
225
+ CRITICAL RULES - YOU MUST FOLLOW THESE EXACTLY:
226
+ 1. Output ONLY executable commands, one per line
227
+ 2. NO explanations, NO markdown code blocks, NO comments, NO extra text
228
+ 3. For multi-step actions, insert WAIT <milliseconds> between commands
229
+ 4. Use GENERIC app names (e.g., "launch settings", "launch chrome", "launch camera")
230
+ 5. Do NOT use platform-specific package IDs - use simple app names
231
+ 6. The system will automatically convert to correct package IDs for each platform
232
+ 5. For URLs, always use: url https://example.com
233
+ 6. For scrolling, use: swipe up OR swipe down (never "scroll")
234
+ 7. For text input, output ONLY the text (never include "type" prefix)
235
+ 8. For home button, output: home (never "press home" or "go home")
236
+ 9. WAIT timings: apps=3000ms, pages=2000ms, UI=1000ms, quick=500ms
237
+ 10. Parse compound requests into individual steps with WAIT between each
238
+
239
+ OUTPUT FORMAT EXAMPLE:
240
+ launch com.apple.Preferences
241
+ WAIT 3000
242
+ swipe up
243
+ WAIT 1000
244
+ home
245
+
246
+ DO NOT include any other text. Start your response with the first command.`;
247
+ }
248
+
249
+ async convertCommand(text, platform = null, providerOverride = null) {
250
+ const provider = providerOverride || this.provider;
251
+
252
+ try {
253
+ switch (provider) {
254
+ case 'local':
255
+ case 'built-in':
256
+ case 'builtin':
257
+ return await this.convertWithLocalAI(text, platform);
258
+ case 'openai':
259
+ return await this.convertWithOpenAI(text, platform);
260
+ case 'gemini':
261
+ return await this.convertWithGemini(text, platform);
262
+ case 'claude':
263
+ return await this.convertWithClaude(text, platform);
264
+ case 'copilot':
265
+ return await this.convertWithCopilot(text, platform);
266
+ case 'groq':
267
+ return await this.convertWithGroq(text, platform);
268
+ case 'cohere':
269
+ return await this.convertWithCohere(text, platform);
270
+ case 'mistral':
271
+ return await this.convertWithMistral(text, platform);
272
+ default:
273
+ throw new Error(`Unknown AI provider: ${provider}`);
274
+ }
275
+ } catch (error) {
276
+ console.error(`Error with ${provider}:`, error.message);
277
+
278
+ // 🎯 GENIUS: Fallback to Local AI if external provider fails
279
+ if (provider !== 'local' && this.localAI) {
280
+ console.log(`🔄 Falling back to Local Built-In AI...`);
281
+ try {
282
+ return await this.convertWithLocalAI(text, platform);
283
+ } catch (fallbackError) {
284
+ console.error('Local AI fallback also failed:', fallbackError.message);
285
+ }
286
+ }
287
+
288
+ throw new Error(`AI conversion failed (${provider}): ${error.message}`);
289
+ }
290
+ }
291
+
292
+ // Get model to use (from env or default)
293
+ getModelForProvider(provider) {
294
+ // If specific model is set, use it
295
+ if (this.model) return this.model;
296
+
297
+ // Otherwise use defaults (optimized for speed)
298
+ const defaults = {
299
+ openai: 'gpt-4o-mini', // Faster than gpt-4o
300
+ gemini: 'gemini-2.5-flash', // Fast and reliable
301
+ claude: 'claude-3-5-sonnet-20241022',
302
+ copilot: 'gpt-4o',
303
+ groq: 'llama-3.3-70b-versatile',
304
+ cohere: 'command-r-plus',
305
+ mistral: 'mistral-large-latest',
306
+ };
307
+
308
+ return defaults[provider] || 'gpt-4';
309
+ }
310
+
311
+ async convertWithOpenAI(text, platform = null) {
312
+ if (!this.openai) {
313
+ throw new Error('OpenAI not configured');
314
+ }
315
+
316
+ const model = this.getModelForProvider('openai');
317
+
318
+ const response = await this.openai.chat.completions.create({
319
+ model: model,
320
+ messages: [
321
+ { role: 'system', content: this.getSystemPrompt(platform) },
322
+ { role: 'user', content: text }
323
+ ],
324
+ temperature: 0.3,
325
+ max_tokens: 500,
326
+ });
327
+
328
+ let convertedText = response.choices[0].message.content.trim();
329
+ const cleanedResponse = this.cleanAIResponse(convertedText);
330
+
331
+ return {
332
+ success: true,
333
+ converted: cleanedResponse,
334
+ provider: 'OpenAI',
335
+ model: model,
336
+ confidence: 0.95,
337
+ processingTime: 'N/A'
338
+ };
339
+ }
340
+
341
+ async convertWithGemini(text, platform = null) {
342
+ if (!this.gemini) {
343
+ throw new Error('Gemini not configured');
344
+ }
345
+
346
+ let modelName = this.getModelForProvider('gemini');
347
+
348
+ try {
349
+ // Use default Gemini config for best accuracy
350
+ const model = this.gemini.getGenerativeModel({ model: modelName });
351
+
352
+ // Build prompt and sanitize for Gemini API (requires ASCII-safe ByteString)
353
+ const rawPrompt = this.getSystemPrompt(platform).replace('{INPUT}', text);
354
+
355
+ // Remove ALL non-ASCII characters that could cause ByteString errors
356
+ const sanitizedPrompt = rawPrompt
357
+ .replace(/[^\x00-\x7F]/g, ' ') // Replace non-ASCII with spaces
358
+ .replace(/\s+/g, ' ') // Normalize multiple spaces
359
+ .trim();
360
+
361
+ const result = await model.generateContent(sanitizedPrompt);
362
+ const response = await result.response;
363
+ let convertedText = response.text().trim();
364
+
365
+ const cleanedResponse = this.cleanAIResponse(convertedText);
366
+
367
+ return {
368
+ success: true,
369
+ converted: cleanedResponse,
370
+ provider: 'Google Gemini',
371
+ model: modelName,
372
+ confidence: 0.9,
373
+ processingTime: 'N/A'
374
+ };
375
+ } catch (error) {
376
+ // If the model fails, try with a faster fallback
377
+ if (modelName !== 'gemini-2.5-flash') {
378
+ console.warn(`Gemini model ${modelName} failed, falling back to gemini-2.5-flash`);
379
+ const fallbackModel = this.gemini.getGenerativeModel({ model: 'gemini-2.5-flash' });
380
+ const prompt = this.getSystemPrompt(platform).replace('{INPUT}', text);
381
+ const sanitizedPrompt = prompt.replace(/[^\x00-\x7F]/g, " ").replace(/\s+/g, ' ').trim();
382
+ const result = await fallbackModel.generateContent(sanitizedPrompt);
383
+ const response = await result.response;
384
+ const cleanedResponse = this.cleanAIResponse(response.text().trim());
385
+
386
+ return {
387
+ success: true,
388
+ converted: cleanedResponse,
389
+ provider: 'Google Gemini',
390
+ model: 'gemini-2.5-flash',
391
+ confidence: 0.85,
392
+ processingTime: 'N/A'
393
+ };
394
+ }
395
+ throw error;
396
+ }
397
+ }
398
+
399
+ async convertWithCopilot(text, platform = null) {
400
+ if (!this.copilot) {
401
+ throw new Error('GitHub Copilot not configured');
402
+ }
403
+
404
+ const model = this.getModelForProvider('copilot');
405
+
406
+ const response = await this.copilot.chat.completions.create({
407
+ model: model,
408
+ messages: [
409
+ { role: 'system', content: this.getSystemPrompt(platform) },
410
+ { role: 'user', content: text }
411
+ ],
412
+ temperature: 0.3,
413
+ max_tokens: 500,
414
+ });
415
+
416
+ let convertedText = response.choices[0].message.content.trim();
417
+ convertedText = this.cleanAIResponse(convertedText);
418
+
419
+ return convertedText;
420
+ }
421
+
422
+ async convertWithClaude(text, platform = null) {
423
+ if (!this.claude) {
424
+ throw new Error('Claude not configured');
425
+ }
426
+
427
+ const model = this.getModelForProvider('claude');
428
+ const systemPrompt = this.getSystemPrompt(platform);
429
+
430
+ const response = await this.claude.messages.create({
431
+ model: model,
432
+ max_tokens: 500,
433
+ system: systemPrompt,
434
+ messages: [
435
+ { role: 'user', content: text }
436
+ ],
437
+ temperature: 0.3,
438
+ });
439
+
440
+ let convertedText = response.content[0].text.trim();
441
+ const cleanedResponse = this.cleanAIResponse(convertedText);
442
+
443
+ return {
444
+ success: true,
445
+ converted: cleanedResponse,
446
+ provider: 'Anthropic Claude',
447
+ model: model,
448
+ confidence: 0.93,
449
+ processingTime: 'N/A'
450
+ };
451
+ }
452
+
453
+ async convertWithGroq(text, platform = null) {
454
+ if (!this.groq) {
455
+ throw new Error('Groq not configured');
456
+ }
457
+
458
+ const model = this.getModelForProvider('groq');
459
+
460
+ const response = await this.groq.chat.completions.create({
461
+ model: model,
462
+ messages: [
463
+ { role: 'system', content: this.getSystemPrompt(platform) },
464
+ { role: 'user', content: text }
465
+ ],
466
+ temperature: 0.3,
467
+ max_tokens: 500,
468
+ });
469
+
470
+ let convertedText = response.choices[0].message.content.trim();
471
+ const cleanedResponse = this.cleanAIResponse(convertedText);
472
+
473
+ return {
474
+ success: true,
475
+ converted: cleanedResponse,
476
+ provider: 'Groq',
477
+ model: model,
478
+ confidence: 0.92,
479
+ processingTime: 'N/A'
480
+ };
481
+ }
482
+
483
+ async convertWithCohere(text, platform = null) {
484
+ if (!this.cohere) {
485
+ throw new Error('Cohere not configured');
486
+ }
487
+
488
+ const model = this.getModelForProvider('cohere');
489
+
490
+ const response = await this.cohere.chat.completions.create({
491
+ model: model,
492
+ messages: [
493
+ { role: 'system', content: this.getSystemPrompt(platform) },
494
+ { role: 'user', content: text }
495
+ ],
496
+ temperature: 0.3,
497
+ max_tokens: 500,
498
+ });
499
+
500
+ let convertedText = response.choices[0].message.content.trim();
501
+ convertedText = this.cleanAIResponse(convertedText);
502
+
503
+ return convertedText;
504
+ }
505
+
506
+ async convertWithMistral(text, platform = null) {
507
+ if (!this.mistral) {
508
+ throw new Error('Mistral AI not configured');
509
+ }
510
+
511
+ const model = this.getModelForProvider('mistral');
512
+
513
+ const response = await this.mistral.chat.completions.create({
514
+ model: model,
515
+ messages: [
516
+ { role: 'system', content: this.getSystemPrompt(platform) },
517
+ { role: 'user', content: text }
518
+ ],
519
+ temperature: 0.3,
520
+ max_tokens: 500,
521
+ });
522
+
523
+ let convertedText = response.choices[0].message.content.trim();
524
+ convertedText = this.cleanAIResponse(convertedText);
525
+
526
+ return convertedText;
527
+ }
528
+
529
+ // Clean up AI responses - remove markdown, explanations, etc.
530
+ cleanAIResponse(text) {
531
+ // Remove markdown code blocks
532
+ text = text.replace(/```[\s\S]*?```/g, '').trim();
533
+ text = text.replace(/```/g, '').trim();
534
+
535
+ // Remove any lines that look like explanations (starting with explanatory text)
536
+ const lines = text.split('\n');
537
+ const cleanedLines = lines.filter(line => {
538
+ const trimmed = line.trim();
539
+ if (!trimmed) return false;
540
+
541
+ // Keep lines that are commands or WAIT
542
+ if (trimmed.startsWith('launch ')) return true;
543
+ if (trimmed.startsWith('kill ')) return true;
544
+ if (trimmed === 'home') return true;
545
+ if (trimmed === 'back') return true;
546
+ if (trimmed.startsWith('url ')) return true;
547
+ if (trimmed.startsWith('click ')) return true;
548
+ if (trimmed.startsWith('tap ')) return true;
549
+ if (trimmed.startsWith('longpress ')) return true;
550
+ if (trimmed.startsWith('swipe ')) return true;
551
+ if (trimmed.startsWith('WAIT ')) return true;
552
+ if (trimmed.startsWith('screenshot')) return true;
553
+ if (trimmed.startsWith('restart')) return true;
554
+ if (trimmed.startsWith('rotate ')) return true;
555
+ if (trimmed.startsWith('darkmode')) return true;
556
+ if (trimmed.startsWith('lightmode')) return true;
557
+ if (trimmed.startsWith('airplane ')) return true;
558
+ if (trimmed.startsWith('wifi ')) return true;
559
+ if (trimmed.startsWith('volume ')) return true;
560
+ if (trimmed === 'getLocators') return true;
561
+ if (trimmed === 'recent') return true;
562
+ if (trimmed === 'notifications') return true;
563
+ if (trimmed === 'quicksettings') return true;
564
+
565
+ // If it doesn't start with a known command, it might be text to type
566
+ // Check if previous line was a command that expects text input
567
+ return true; // For now, include it (could be text to type)
568
+ });
569
+
570
+ return cleanedLines.join('\n').trim();
571
+ }
572
+
573
+ getAvailableProviders() {
574
+ const available = [];
575
+
576
+ if (this.openai) available.push({ id: 'openai', name: 'OpenAI', icon: '🤖' });
577
+ if (this.gemini) available.push({ id: 'gemini', name: 'Google Gemini', icon: '✨' });
578
+ if (this.claude) available.push({ id: 'claude', name: 'Anthropic Claude', icon: '🧠' });
579
+ if (this.copilot) available.push({ id: 'copilot', name: 'GitHub Copilot', icon: '🐙' });
580
+ if (this.groq) available.push({ id: 'groq', name: 'Groq', icon: '⚡' });
581
+ if (this.cohere) available.push({ id: 'cohere', name: 'Cohere', icon: '🌊' });
582
+ if (this.mistral) available.push({ id: 'mistral', name: 'Mistral AI', icon: '🌬️' });
583
+
584
+ return available;
585
+ }
586
+
587
+ setProvider(provider, model = null) {
588
+ const available = this.getAvailableProviders().map(p => p.id);
589
+ if (available.includes(provider)) {
590
+ this.provider = provider;
591
+ if (model) {
592
+ this.model = model;
593
+ }
594
+ return true;
595
+ }
596
+ return false;
597
+ }
598
+
599
+ getCurrentProvider() {
600
+ return {
601
+ id: this.provider,
602
+ name: this.getProviderName(this.provider),
603
+ model: this.model || this.getModelForProvider(this.provider),
604
+ available: this.getAvailableProviders(),
605
+ };
606
+ }
607
+
608
+ getProviderName(providerId) {
609
+ const names = {
610
+ local: 'Local Built-In AI',
611
+ 'built-in': 'Local Built-In AI',
612
+ builtin: 'Local Built-In AI',
613
+ openai: 'OpenAI',
614
+ gemini: 'Google Gemini',
615
+ claude: 'Anthropic Claude',
616
+ copilot: 'GitHub Copilot',
617
+ groq: 'Groq',
618
+ cohere: 'Cohere',
619
+ mistral: 'Mistral AI',
620
+ };
621
+ return names[providerId] || 'Unknown';
622
+ }
623
+
624
+ /**
625
+ * 🤖 LOCAL BUILT-IN AI CONVERSION METHOD
626
+ * Zero-cost, offline, lightning-fast natural language processing
627
+ */
628
+ async convertWithLocalAI(text, platform = null) {
629
+ console.log(`🤖 Converting with Local Built-In AI: "${text}"`);
630
+
631
+ try {
632
+ const devices = platform ? [{ platform }] : [];
633
+ const result = await this.localAI.convertCommand(text, devices);
634
+
635
+ if (result.success) {
636
+ return {
637
+ converted: result.converted,
638
+ provider: 'Local Built-In AI',
639
+ confidence: result.confidence,
640
+ processingTime: result.processingTime,
641
+ model: 'built-in-patterns',
642
+ cost: 0 // Always free!
643
+ };
644
+ } else {
645
+ throw new Error(result.error || 'Local AI conversion failed');
646
+ }
647
+ } catch (error) {
648
+ console.error('Local AI conversion error:', error.message);
649
+ throw error;
650
+ }
651
+ }
652
+ }
653
+
654
+ module.exports = AIProviderManager;