mcp-maestro-mobile-ai 1.4.0 → 1.6.0

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.
@@ -11,6 +11,15 @@ import { runMaestroFlow, checkDeviceConnection, checkAppInstalled, getConfig } f
11
11
  import { validateMaestroYaml } from './validateTools.js';
12
12
  import { validateYamlStructure } from '../utils/yamlTemplate.js';
13
13
  import { generateReport, getReportsDir, listReports } from '../utils/reportGenerator.js';
14
+ import {
15
+ lookupCache,
16
+ saveToCache,
17
+ updateCacheUsage,
18
+ listCache as listYamlCache,
19
+ clearCache as clearYamlCache,
20
+ getCacheStats,
21
+ deleteFromCache,
22
+ } from '../utils/yamlCache.js';
14
23
 
15
24
  const __filename = fileURLToPath(import.meta.url);
16
25
  const __dirname = dirname(__filename);
@@ -160,20 +169,55 @@ Please regenerate the YAML following these rules.
160
169
 
161
170
  logger.info(`Test ${result.success ? 'passed' : 'failed'}: ${testName}`);
162
171
 
172
+ // Build response
173
+ const response = {
174
+ success: result.success,
175
+ name: testName,
176
+ duration: result.duration,
177
+ attempts: result.attempts || 1,
178
+ error: result.error || null,
179
+ screenshot: result.screenshot || null,
180
+ output: result.output ? result.output.substring(0, 500) : null,
181
+ runId,
182
+ };
183
+
184
+ // If test passed and we have a prompt, automatically save to cache
185
+ if (result.success && options.prompt && !options.fromCache) {
186
+ try {
187
+ const cacheResult = await saveToCache(
188
+ options.prompt,
189
+ yamlContent,
190
+ testName,
191
+ appId
192
+ );
193
+ if (cacheResult.success) {
194
+ response.cached = true;
195
+ response.cacheHash = cacheResult.hash;
196
+ response.cacheMessage = '✅ Test passed! YAML automatically saved to cache for faster future execution.';
197
+ logger.info(`Auto-cached successful test: ${testName} (hash: ${cacheResult.hash})`);
198
+ }
199
+ } catch (cacheError) {
200
+ logger.warn(`Failed to auto-cache test: ${testName}`, { error: cacheError.message });
201
+ }
202
+ }
203
+
204
+ // If this was from cache, include that info
205
+ if (options.fromCache) {
206
+ response.fromCache = true;
207
+ response.cacheMessage = '⚡ Executed from cached YAML (faster execution)';
208
+ }
209
+
210
+ // If test failed, indicate it was not cached
211
+ if (!result.success) {
212
+ response.cached = false;
213
+ response.cacheMessage = '❌ Test failed - not saved to cache. Fix the issue and re-run to cache.';
214
+ }
215
+
163
216
  return {
164
217
  content: [
165
218
  {
166
219
  type: 'text',
167
- text: JSON.stringify({
168
- success: result.success,
169
- name: testName,
170
- duration: result.duration,
171
- attempts: result.attempts || 1,
172
- error: result.error || null,
173
- screenshot: result.screenshot || null,
174
- output: result.output ? result.output.substring(0, 500) : null,
175
- runId,
176
- }),
220
+ text: JSON.stringify(response),
177
221
  },
178
222
  ],
179
223
  };
@@ -526,37 +570,363 @@ export async function runTestSuiteWithReport(tests, options = {}) {
526
570
  const passed = results.filter(r => r.success).length;
527
571
  const failed = results.length - passed;
528
572
 
573
+ // Auto-cache successful tests
574
+ const cachedTests = [];
575
+ for (let i = 0; i < results.length; i++) {
576
+ const result = results[i];
577
+ const test = tests[i];
578
+
579
+ if (result.success && test.prompt) {
580
+ try {
581
+ const cacheResult = await saveToCache(
582
+ test.prompt,
583
+ test.yaml,
584
+ test.name,
585
+ options.appId
586
+ );
587
+ if (cacheResult.success) {
588
+ cachedTests.push({
589
+ name: test.name,
590
+ hash: cacheResult.hash,
591
+ });
592
+ logger.info(`Auto-cached successful test: ${test.name} (hash: ${cacheResult.hash})`);
593
+ }
594
+ } catch (cacheError) {
595
+ logger.warn(`Failed to auto-cache test: ${test.name}`, { error: cacheError.message });
596
+ }
597
+ }
598
+ }
599
+
600
+ // Build response
601
+ const response = {
602
+ success: failed === 0,
603
+ totalDuration: `${totalDuration}s`,
604
+ summary: {
605
+ total: results.length,
606
+ passed,
607
+ failed,
608
+ passRate: reportResult.summary.passRate,
609
+ },
610
+ report: {
611
+ reportId: reportResult.reportId,
612
+ htmlPath: reportResult.htmlPath,
613
+ jsonPath: reportResult.jsonPath,
614
+ message: `📊 Report generated: ${reportResult.htmlPath}`,
615
+ },
616
+ tests: results.map(r => ({
617
+ name: r.name,
618
+ success: r.success,
619
+ duration: r.duration,
620
+ error: r.error || null,
621
+ })),
622
+ };
623
+
624
+ // Add cache info
625
+ if (cachedTests.length > 0) {
626
+ response.cached = {
627
+ count: cachedTests.length,
628
+ tests: cachedTests,
629
+ message: `✅ ${cachedTests.length} successful test(s) automatically saved to cache for faster future execution.`,
630
+ };
631
+ }
632
+
633
+ if (failed > 0) {
634
+ response.notCached = {
635
+ count: failed,
636
+ message: `❌ ${failed} failed test(s) not saved to cache. Fix issues and re-run to cache.`,
637
+ };
638
+ }
639
+
640
+ return {
641
+ content: [
642
+ {
643
+ type: 'text',
644
+ text: JSON.stringify(response),
645
+ },
646
+ ],
647
+ };
648
+ } catch (error) {
649
+ logger.error('Test suite with report error', { error: error.message });
529
650
  return {
530
651
  content: [
531
652
  {
532
653
  type: 'text',
533
654
  text: JSON.stringify({
534
- success: failed === 0,
535
- totalDuration: `${totalDuration}s`,
536
- summary: {
537
- total: results.length,
538
- passed,
539
- failed,
540
- passRate: reportResult.summary.passRate,
541
- },
542
- report: {
543
- reportId: reportResult.reportId,
544
- htmlPath: reportResult.htmlPath,
545
- jsonPath: reportResult.jsonPath,
546
- message: `📊 Report generated: ${reportResult.htmlPath}`,
547
- },
548
- tests: results.map(r => ({
549
- name: r.name,
550
- success: r.success,
551
- duration: r.duration,
552
- error: r.error || null,
553
- })),
655
+ success: false,
656
+ error: error.message,
657
+ }),
658
+ },
659
+ ],
660
+ };
661
+ }
662
+ }
663
+
664
+ /**
665
+ * Run a test from cache or generate new
666
+ * Checks cache first, falls back to provided YAML
667
+ */
668
+ export async function runTestWithCache(prompt, yamlContent, testName, options = {}) {
669
+ const appId = options.appId || null;
670
+
671
+ // Check cache first
672
+ const cached = await lookupCache(prompt, appId);
673
+
674
+ if (cached) {
675
+ logger.info(`Using cached YAML for: ${testName}`);
676
+
677
+ // Update usage stats
678
+ await updateCacheUsage(cached.hash);
679
+
680
+ // Run with cached YAML
681
+ return runTest(cached.yaml, testName, {
682
+ ...options,
683
+ fromCache: true,
684
+ promptHash: cached.hash,
685
+ });
686
+ }
687
+
688
+ // No cache, run with provided YAML
689
+ return runTest(yamlContent, testName, {
690
+ ...options,
691
+ prompt,
692
+ fromCache: false,
693
+ });
694
+ }
695
+
696
+ /**
697
+ * Save a test to cache after successful execution
698
+ */
699
+ export async function saveTestToCache(prompt, yamlContent, testName, appId = null) {
700
+ try {
701
+ const result = await saveToCache(prompt, yamlContent, testName, appId);
702
+
703
+ return {
704
+ content: [
705
+ {
706
+ type: 'text',
707
+ text: JSON.stringify({
708
+ success: result.success,
709
+ hash: result.hash,
710
+ message: result.success
711
+ ? `✅ Test cached! Future runs of this prompt will use the saved YAML automatically.`
712
+ : `❌ Failed to cache: ${result.error}`,
713
+ hint: result.success
714
+ ? 'When you run the same prompt again, the cached YAML will be used for faster execution.'
715
+ : null,
716
+ }),
717
+ },
718
+ ],
719
+ };
720
+ } catch (error) {
721
+ return {
722
+ content: [
723
+ {
724
+ type: 'text',
725
+ text: JSON.stringify({
726
+ success: false,
727
+ error: error.message,
728
+ }),
729
+ },
730
+ ],
731
+ };
732
+ }
733
+ }
734
+
735
+ /**
736
+ * List all cached tests
737
+ */
738
+ export async function listCachedTests() {
739
+ try {
740
+ const result = await listYamlCache();
741
+
742
+ return {
743
+ content: [
744
+ {
745
+ type: 'text',
746
+ text: JSON.stringify({
747
+ success: result.success,
748
+ count: result.count,
749
+ cacheDir: result.cacheDir,
750
+ tests: result.entries?.map(e => ({
751
+ hash: e.hash,
752
+ testName: e.testName,
753
+ appId: e.appId,
754
+ prompt: e.prompt,
755
+ cachedAt: e.cachedAt,
756
+ lastUsed: e.lastUsed,
757
+ executionCount: e.executionCount,
758
+ })) || [],
759
+ message: result.count > 0
760
+ ? `Found ${result.count} cached test(s). These will be reused automatically when the same prompt is run.`
761
+ : 'No cached tests. Run successful tests and save them for faster future execution.',
762
+ }),
763
+ },
764
+ ],
765
+ };
766
+ } catch (error) {
767
+ return {
768
+ content: [
769
+ {
770
+ type: 'text',
771
+ text: JSON.stringify({
772
+ success: false,
773
+ error: error.message,
774
+ }),
775
+ },
776
+ ],
777
+ };
778
+ }
779
+ }
780
+
781
+ /**
782
+ * Clear all cached tests
783
+ */
784
+ export async function clearTestCache() {
785
+ try {
786
+ const result = await clearYamlCache();
787
+
788
+ return {
789
+ content: [
790
+ {
791
+ type: 'text',
792
+ text: JSON.stringify({
793
+ success: result.success,
794
+ cleared: result.cleared,
795
+ message: result.success
796
+ ? `✅ Cleared ${result.cleared} cached test(s). All tests will generate fresh YAML on next run.`
797
+ : `❌ Failed to clear cache: ${result.error}`,
798
+ }),
799
+ },
800
+ ],
801
+ };
802
+ } catch (error) {
803
+ return {
804
+ content: [
805
+ {
806
+ type: 'text',
807
+ text: JSON.stringify({
808
+ success: false,
809
+ error: error.message,
810
+ }),
811
+ },
812
+ ],
813
+ };
814
+ }
815
+ }
816
+
817
+ /**
818
+ * Delete a specific cached test
819
+ */
820
+ export async function deleteCachedTest(hash) {
821
+ try {
822
+ const result = await deleteFromCache(hash);
823
+
824
+ return {
825
+ content: [
826
+ {
827
+ type: 'text',
828
+ text: JSON.stringify({
829
+ success: result.success,
830
+ message: result.success
831
+ ? `✅ Cached test deleted. A new YAML will be generated on next run.`
832
+ : `❌ ${result.error}`,
833
+ }),
834
+ },
835
+ ],
836
+ };
837
+ } catch (error) {
838
+ return {
839
+ content: [
840
+ {
841
+ type: 'text',
842
+ text: JSON.stringify({
843
+ success: false,
844
+ error: error.message,
845
+ }),
846
+ },
847
+ ],
848
+ };
849
+ }
850
+ }
851
+
852
+ /**
853
+ * Get cache statistics
854
+ */
855
+ export async function getTestCacheStats() {
856
+ try {
857
+ const result = await getCacheStats();
858
+
859
+ return {
860
+ content: [
861
+ {
862
+ type: 'text',
863
+ text: JSON.stringify({
864
+ success: result.success,
865
+ stats: result.stats,
866
+ message: result.stats?.totalCached > 0
867
+ ? `Cache has ${result.stats.totalCached} test(s) with ${result.stats.totalExecutions} total executions.`
868
+ : 'Cache is empty.',
869
+ }),
870
+ },
871
+ ],
872
+ };
873
+ } catch (error) {
874
+ return {
875
+ content: [
876
+ {
877
+ type: 'text',
878
+ text: JSON.stringify({
879
+ success: false,
880
+ error: error.message,
881
+ }),
882
+ },
883
+ ],
884
+ };
885
+ }
886
+ }
887
+
888
+ /**
889
+ * Lookup cached YAML for a prompt
890
+ */
891
+ export async function lookupCachedTest(prompt, appId = null) {
892
+ try {
893
+ const cached = await lookupCache(prompt, appId);
894
+
895
+ if (cached) {
896
+ return {
897
+ content: [
898
+ {
899
+ type: 'text',
900
+ text: JSON.stringify({
901
+ success: true,
902
+ found: true,
903
+ hash: cached.hash,
904
+ testName: cached.testName,
905
+ appId: cached.appId,
906
+ cachedAt: cached.cachedAt,
907
+ lastUsed: cached.lastUsed,
908
+ executionCount: cached.executionCount,
909
+ yaml: cached.yaml,
910
+ message: '⚡ Found cached YAML for this prompt. Will be used for faster execution.',
911
+ }),
912
+ },
913
+ ],
914
+ };
915
+ }
916
+
917
+ return {
918
+ content: [
919
+ {
920
+ type: 'text',
921
+ text: JSON.stringify({
922
+ success: true,
923
+ found: false,
924
+ message: 'No cached YAML found for this prompt. A new YAML will be generated.',
554
925
  }),
555
926
  },
556
927
  ],
557
928
  };
558
929
  } catch (error) {
559
- logger.error('Test suite with report error', { error: error.message });
560
930
  return {
561
931
  content: [
562
932
  {
@@ -577,4 +947,12 @@ export default {
577
947
  generateTestReport,
578
948
  listTestReports,
579
949
  runTestSuiteWithReport,
950
+ // Cache management
951
+ runTestWithCache,
952
+ saveTestToCache,
953
+ listCachedTests,
954
+ clearTestCache,
955
+ deleteCachedTest,
956
+ getTestCacheStats,
957
+ lookupCachedTest,
580
958
  };