devicely 2.2.12 → 2.2.14

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