mindheal 1.0.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.
Files changed (255) hide show
  1. package/.env.example +48 -0
  2. package/CHANGELOG.md +27 -0
  3. package/LICENSE +21 -0
  4. package/README.md +481 -0
  5. package/dist/cjs/ai/ai-provider.js +46 -0
  6. package/dist/cjs/ai/ai-provider.js.map +1 -0
  7. package/dist/cjs/ai/anthropic-provider.js +106 -0
  8. package/dist/cjs/ai/anthropic-provider.js.map +1 -0
  9. package/dist/cjs/ai/azure-openai-provider.js +130 -0
  10. package/dist/cjs/ai/azure-openai-provider.js.map +1 -0
  11. package/dist/cjs/ai/bedrock-provider.js +183 -0
  12. package/dist/cjs/ai/bedrock-provider.js.map +1 -0
  13. package/dist/cjs/ai/deepseek-provider.js +118 -0
  14. package/dist/cjs/ai/deepseek-provider.js.map +1 -0
  15. package/dist/cjs/ai/gemini-provider.js +129 -0
  16. package/dist/cjs/ai/gemini-provider.js.map +1 -0
  17. package/dist/cjs/ai/groq-provider.js +118 -0
  18. package/dist/cjs/ai/groq-provider.js.map +1 -0
  19. package/dist/cjs/ai/meta-provider.js +118 -0
  20. package/dist/cjs/ai/meta-provider.js.map +1 -0
  21. package/dist/cjs/ai/ollama-provider.js +127 -0
  22. package/dist/cjs/ai/ollama-provider.js.map +1 -0
  23. package/dist/cjs/ai/openai-provider.js +117 -0
  24. package/dist/cjs/ai/openai-provider.js.map +1 -0
  25. package/dist/cjs/ai/perplexity-provider.js +118 -0
  26. package/dist/cjs/ai/perplexity-provider.js.map +1 -0
  27. package/dist/cjs/ai/prompt-templates.js +174 -0
  28. package/dist/cjs/ai/prompt-templates.js.map +1 -0
  29. package/dist/cjs/ai/qwen-provider.js +118 -0
  30. package/dist/cjs/ai/qwen-provider.js.map +1 -0
  31. package/dist/cjs/analytics/healing-analytics.js +263 -0
  32. package/dist/cjs/analytics/healing-analytics.js.map +1 -0
  33. package/dist/cjs/cli/init.js +517 -0
  34. package/dist/cjs/cli/init.js.map +1 -0
  35. package/dist/cjs/config/config-loader.js +135 -0
  36. package/dist/cjs/config/config-loader.js.map +1 -0
  37. package/dist/cjs/config/defaults.js +109 -0
  38. package/dist/cjs/config/defaults.js.map +1 -0
  39. package/dist/cjs/core/dom-snapshot.js +280 -0
  40. package/dist/cjs/core/dom-snapshot.js.map +1 -0
  41. package/dist/cjs/core/enterprise-strategy.js +702 -0
  42. package/dist/cjs/core/enterprise-strategy.js.map +1 -0
  43. package/dist/cjs/core/healer.js +283 -0
  44. package/dist/cjs/core/healer.js.map +1 -0
  45. package/dist/cjs/core/interceptor.js +945 -0
  46. package/dist/cjs/core/interceptor.js.map +1 -0
  47. package/dist/cjs/core/locator-analyzer.js +172 -0
  48. package/dist/cjs/core/locator-analyzer.js.map +1 -0
  49. package/dist/cjs/core/locator-strategies.js +891 -0
  50. package/dist/cjs/core/locator-strategies.js.map +1 -0
  51. package/dist/cjs/core/self-heal-cache.js +178 -0
  52. package/dist/cjs/core/self-heal-cache.js.map +1 -0
  53. package/dist/cjs/core/smart-retry.js +248 -0
  54. package/dist/cjs/core/smart-retry.js.map +1 -0
  55. package/dist/cjs/core/visual-verification.js +262 -0
  56. package/dist/cjs/core/visual-verification.js.map +1 -0
  57. package/dist/cjs/git/code-modifier.js +184 -0
  58. package/dist/cjs/git/code-modifier.js.map +1 -0
  59. package/dist/cjs/git/git-operations.js +145 -0
  60. package/dist/cjs/git/git-operations.js.map +1 -0
  61. package/dist/cjs/git/pr-creator.js +190 -0
  62. package/dist/cjs/git/pr-creator.js.map +1 -0
  63. package/dist/cjs/index.js +97 -0
  64. package/dist/cjs/index.js.map +1 -0
  65. package/dist/cjs/rag/context-retriever.js +289 -0
  66. package/dist/cjs/rag/context-retriever.js.map +1 -0
  67. package/dist/cjs/rag/embeddings.js +82 -0
  68. package/dist/cjs/rag/embeddings.js.map +1 -0
  69. package/dist/cjs/rag/knowledge-store.js +159 -0
  70. package/dist/cjs/rag/knowledge-store.js.map +1 -0
  71. package/dist/cjs/reporters/heal-report.js +279 -0
  72. package/dist/cjs/reporters/heal-report.js.map +1 -0
  73. package/dist/cjs/reporters/heal-reporter.js +294 -0
  74. package/dist/cjs/reporters/heal-reporter.js.map +1 -0
  75. package/dist/cjs/server/review-server.js +166 -0
  76. package/dist/cjs/server/review-server.js.map +1 -0
  77. package/dist/cjs/server/routes.js +92 -0
  78. package/dist/cjs/server/routes.js.map +1 -0
  79. package/dist/cjs/utils/environment.js +57 -0
  80. package/dist/cjs/utils/environment.js.map +1 -0
  81. package/dist/cjs/utils/file-lock.js +136 -0
  82. package/dist/cjs/utils/file-lock.js.map +1 -0
  83. package/dist/cjs/utils/file-utils.js +49 -0
  84. package/dist/cjs/utils/file-utils.js.map +1 -0
  85. package/dist/cjs/utils/logger.js +78 -0
  86. package/dist/cjs/utils/logger.js.map +1 -0
  87. package/dist/esm/ai/ai-provider.js +44 -0
  88. package/dist/esm/ai/ai-provider.js.map +1 -0
  89. package/dist/esm/ai/anthropic-provider.js +104 -0
  90. package/dist/esm/ai/anthropic-provider.js.map +1 -0
  91. package/dist/esm/ai/azure-openai-provider.js +128 -0
  92. package/dist/esm/ai/azure-openai-provider.js.map +1 -0
  93. package/dist/esm/ai/bedrock-provider.js +181 -0
  94. package/dist/esm/ai/bedrock-provider.js.map +1 -0
  95. package/dist/esm/ai/deepseek-provider.js +116 -0
  96. package/dist/esm/ai/deepseek-provider.js.map +1 -0
  97. package/dist/esm/ai/gemini-provider.js +127 -0
  98. package/dist/esm/ai/gemini-provider.js.map +1 -0
  99. package/dist/esm/ai/groq-provider.js +116 -0
  100. package/dist/esm/ai/groq-provider.js.map +1 -0
  101. package/dist/esm/ai/meta-provider.js +116 -0
  102. package/dist/esm/ai/meta-provider.js.map +1 -0
  103. package/dist/esm/ai/ollama-provider.js +125 -0
  104. package/dist/esm/ai/ollama-provider.js.map +1 -0
  105. package/dist/esm/ai/openai-provider.js +115 -0
  106. package/dist/esm/ai/openai-provider.js.map +1 -0
  107. package/dist/esm/ai/perplexity-provider.js +116 -0
  108. package/dist/esm/ai/perplexity-provider.js.map +1 -0
  109. package/dist/esm/ai/prompt-templates.js +171 -0
  110. package/dist/esm/ai/prompt-templates.js.map +1 -0
  111. package/dist/esm/ai/qwen-provider.js +116 -0
  112. package/dist/esm/ai/qwen-provider.js.map +1 -0
  113. package/dist/esm/analytics/healing-analytics.js +261 -0
  114. package/dist/esm/analytics/healing-analytics.js.map +1 -0
  115. package/dist/esm/cli/init.js +495 -0
  116. package/dist/esm/cli/init.js.map +1 -0
  117. package/dist/esm/config/config-loader.js +132 -0
  118. package/dist/esm/config/config-loader.js.map +1 -0
  119. package/dist/esm/config/defaults.js +107 -0
  120. package/dist/esm/config/defaults.js.map +1 -0
  121. package/dist/esm/core/dom-snapshot.js +278 -0
  122. package/dist/esm/core/dom-snapshot.js.map +1 -0
  123. package/dist/esm/core/enterprise-strategy.js +695 -0
  124. package/dist/esm/core/enterprise-strategy.js.map +1 -0
  125. package/dist/esm/core/healer.js +281 -0
  126. package/dist/esm/core/healer.js.map +1 -0
  127. package/dist/esm/core/interceptor.js +940 -0
  128. package/dist/esm/core/interceptor.js.map +1 -0
  129. package/dist/esm/core/locator-analyzer.js +169 -0
  130. package/dist/esm/core/locator-analyzer.js.map +1 -0
  131. package/dist/esm/core/locator-strategies.js +882 -0
  132. package/dist/esm/core/locator-strategies.js.map +1 -0
  133. package/dist/esm/core/self-heal-cache.js +176 -0
  134. package/dist/esm/core/self-heal-cache.js.map +1 -0
  135. package/dist/esm/core/smart-retry.js +246 -0
  136. package/dist/esm/core/smart-retry.js.map +1 -0
  137. package/dist/esm/core/visual-verification.js +260 -0
  138. package/dist/esm/core/visual-verification.js.map +1 -0
  139. package/dist/esm/git/code-modifier.js +182 -0
  140. package/dist/esm/git/code-modifier.js.map +1 -0
  141. package/dist/esm/git/git-operations.js +143 -0
  142. package/dist/esm/git/git-operations.js.map +1 -0
  143. package/dist/esm/git/pr-creator.js +188 -0
  144. package/dist/esm/git/pr-creator.js.map +1 -0
  145. package/dist/esm/index.js +37 -0
  146. package/dist/esm/index.js.map +1 -0
  147. package/dist/esm/rag/context-retriever.js +287 -0
  148. package/dist/esm/rag/context-retriever.js.map +1 -0
  149. package/dist/esm/rag/embeddings.js +77 -0
  150. package/dist/esm/rag/embeddings.js.map +1 -0
  151. package/dist/esm/rag/knowledge-store.js +157 -0
  152. package/dist/esm/rag/knowledge-store.js.map +1 -0
  153. package/dist/esm/reporters/heal-report.js +277 -0
  154. package/dist/esm/reporters/heal-report.js.map +1 -0
  155. package/dist/esm/reporters/heal-reporter.js +290 -0
  156. package/dist/esm/reporters/heal-reporter.js.map +1 -0
  157. package/dist/esm/server/review-server.js +164 -0
  158. package/dist/esm/server/review-server.js.map +1 -0
  159. package/dist/esm/server/routes.js +90 -0
  160. package/dist/esm/server/routes.js.map +1 -0
  161. package/dist/esm/utils/environment.js +53 -0
  162. package/dist/esm/utils/environment.js.map +1 -0
  163. package/dist/esm/utils/file-lock.js +134 -0
  164. package/dist/esm/utils/file-lock.js.map +1 -0
  165. package/dist/esm/utils/file-utils.js +43 -0
  166. package/dist/esm/utils/file-utils.js.map +1 -0
  167. package/dist/esm/utils/logger.js +75 -0
  168. package/dist/esm/utils/logger.js.map +1 -0
  169. package/dist/types/ai/ai-provider.d.ts +4 -0
  170. package/dist/types/ai/ai-provider.d.ts.map +1 -0
  171. package/dist/types/ai/anthropic-provider.d.ts +11 -0
  172. package/dist/types/ai/anthropic-provider.d.ts.map +1 -0
  173. package/dist/types/ai/azure-openai-provider.d.ts +13 -0
  174. package/dist/types/ai/azure-openai-provider.d.ts.map +1 -0
  175. package/dist/types/ai/bedrock-provider.d.ts +14 -0
  176. package/dist/types/ai/bedrock-provider.d.ts.map +1 -0
  177. package/dist/types/ai/deepseek-provider.d.ts +12 -0
  178. package/dist/types/ai/deepseek-provider.d.ts.map +1 -0
  179. package/dist/types/ai/gemini-provider.d.ts +12 -0
  180. package/dist/types/ai/gemini-provider.d.ts.map +1 -0
  181. package/dist/types/ai/groq-provider.d.ts +12 -0
  182. package/dist/types/ai/groq-provider.d.ts.map +1 -0
  183. package/dist/types/ai/meta-provider.d.ts +12 -0
  184. package/dist/types/ai/meta-provider.d.ts.map +1 -0
  185. package/dist/types/ai/ollama-provider.d.ts +10 -0
  186. package/dist/types/ai/ollama-provider.d.ts.map +1 -0
  187. package/dist/types/ai/openai-provider.d.ts +11 -0
  188. package/dist/types/ai/openai-provider.d.ts.map +1 -0
  189. package/dist/types/ai/perplexity-provider.d.ts +12 -0
  190. package/dist/types/ai/perplexity-provider.d.ts.map +1 -0
  191. package/dist/types/ai/prompt-templates.d.ts +11 -0
  192. package/dist/types/ai/prompt-templates.d.ts.map +1 -0
  193. package/dist/types/ai/qwen-provider.d.ts +12 -0
  194. package/dist/types/ai/qwen-provider.d.ts.map +1 -0
  195. package/dist/types/analytics/healing-analytics.d.ts +36 -0
  196. package/dist/types/analytics/healing-analytics.d.ts.map +1 -0
  197. package/dist/types/cli/init.d.ts +15 -0
  198. package/dist/types/cli/init.d.ts.map +1 -0
  199. package/dist/types/config/config-loader.d.ts +4 -0
  200. package/dist/types/config/config-loader.d.ts.map +1 -0
  201. package/dist/types/config/defaults.d.ts +3 -0
  202. package/dist/types/config/defaults.d.ts.map +1 -0
  203. package/dist/types/core/dom-snapshot.d.ts +12 -0
  204. package/dist/types/core/dom-snapshot.d.ts.map +1 -0
  205. package/dist/types/core/enterprise-strategy.d.ts +56 -0
  206. package/dist/types/core/enterprise-strategy.d.ts.map +1 -0
  207. package/dist/types/core/healer.d.ts +52 -0
  208. package/dist/types/core/healer.d.ts.map +1 -0
  209. package/dist/types/core/interceptor.d.ts +64 -0
  210. package/dist/types/core/interceptor.d.ts.map +1 -0
  211. package/dist/types/core/locator-analyzer.d.ts +31 -0
  212. package/dist/types/core/locator-analyzer.d.ts.map +1 -0
  213. package/dist/types/core/locator-strategies.d.ts +45 -0
  214. package/dist/types/core/locator-strategies.d.ts.map +1 -0
  215. package/dist/types/core/self-heal-cache.d.ts +51 -0
  216. package/dist/types/core/self-heal-cache.d.ts.map +1 -0
  217. package/dist/types/core/smart-retry.d.ts +64 -0
  218. package/dist/types/core/smart-retry.d.ts.map +1 -0
  219. package/dist/types/core/visual-verification.d.ts +46 -0
  220. package/dist/types/core/visual-verification.d.ts.map +1 -0
  221. package/dist/types/git/code-modifier.d.ts +51 -0
  222. package/dist/types/git/code-modifier.d.ts.map +1 -0
  223. package/dist/types/git/git-operations.d.ts +40 -0
  224. package/dist/types/git/git-operations.d.ts.map +1 -0
  225. package/dist/types/git/pr-creator.d.ts +27 -0
  226. package/dist/types/git/pr-creator.d.ts.map +1 -0
  227. package/dist/types/index.d.ts +40 -0
  228. package/dist/types/index.d.ts.map +1 -0
  229. package/dist/types/rag/context-retriever.d.ts +69 -0
  230. package/dist/types/rag/context-retriever.d.ts.map +1 -0
  231. package/dist/types/rag/embeddings.d.ts +32 -0
  232. package/dist/types/rag/embeddings.d.ts.map +1 -0
  233. package/dist/types/rag/index.d.ts +12 -0
  234. package/dist/types/rag/index.d.ts.map +1 -0
  235. package/dist/types/rag/knowledge-store.d.ts +38 -0
  236. package/dist/types/rag/knowledge-store.d.ts.map +1 -0
  237. package/dist/types/reporters/heal-report.d.ts +29 -0
  238. package/dist/types/reporters/heal-report.d.ts.map +1 -0
  239. package/dist/types/reporters/heal-reporter.d.ts +49 -0
  240. package/dist/types/reporters/heal-reporter.d.ts.map +1 -0
  241. package/dist/types/server/review-server.d.ts +20 -0
  242. package/dist/types/server/review-server.d.ts.map +1 -0
  243. package/dist/types/server/routes.d.ts +4 -0
  244. package/dist/types/server/routes.d.ts.map +1 -0
  245. package/dist/types/types/index.d.ts +433 -0
  246. package/dist/types/types/index.d.ts.map +1 -0
  247. package/dist/types/utils/environment.d.ts +10 -0
  248. package/dist/types/utils/environment.d.ts.map +1 -0
  249. package/dist/types/utils/file-lock.d.ts +37 -0
  250. package/dist/types/utils/file-lock.d.ts.map +1 -0
  251. package/dist/types/utils/file-utils.d.ts +7 -0
  252. package/dist/types/utils/file-utils.d.ts.map +1 -0
  253. package/dist/types/utils/logger.d.ts +9 -0
  254. package/dist/types/utils/logger.d.ts.map +1 -0
  255. package/package.json +106 -0
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ var promptTemplates = require('./prompt-templates.js');
4
+ var logger = require('../utils/logger.js');
5
+
6
+ const DEFAULT_MODEL = 'sonar-pro';
7
+ const DEFAULT_MAX_TOKENS = 1024;
8
+ const DEFAULT_TEMPERATURE = 0;
9
+ const DEFAULT_API_URL = 'https://api.perplexity.ai/chat/completions';
10
+ const REQUEST_TIMEOUT_MS = 30000;
11
+ class PerplexityProvider {
12
+ constructor(config) {
13
+ this.name = 'perplexity';
14
+ if (!config.apiKey) {
15
+ throw new Error('[MindHeal] Perplexity API key is required');
16
+ }
17
+ this.apiKey = config.apiKey;
18
+ this.model = config.model ?? DEFAULT_MODEL;
19
+ this.maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;
20
+ this.temperature = config.temperature ?? DEFAULT_TEMPERATURE;
21
+ this.apiUrl = config.baseUrl ?? DEFAULT_API_URL;
22
+ }
23
+ async suggestLocator(request) {
24
+ const fullPrompt = promptTemplates.buildHealingPrompt(request);
25
+ const startTime = Date.now();
26
+ logger.logger.debug('Perplexity API: sending healing request', {
27
+ model: this.model,
28
+ originalLocator: request.originalLocator.selector,
29
+ pageUrl: request.pageUrl,
30
+ });
31
+ // Split the combined prompt into system + user messages at the separator
32
+ const separatorIndex = fullPrompt.indexOf('\n\n---\n\n');
33
+ let systemContent;
34
+ let userContent;
35
+ if (separatorIndex !== -1) {
36
+ systemContent = fullPrompt.slice(0, separatorIndex);
37
+ userContent = fullPrompt.slice(separatorIndex + 7); // length of '\n\n---\n\n'
38
+ }
39
+ else {
40
+ systemContent = 'You are an expert Playwright test engineer. Respond with only valid JSON.';
41
+ userContent = fullPrompt;
42
+ }
43
+ const messages = [
44
+ { role: 'system', content: systemContent },
45
+ { role: 'user', content: userContent },
46
+ ];
47
+ const body = JSON.stringify({
48
+ model: this.model,
49
+ max_tokens: this.maxTokens,
50
+ temperature: this.temperature,
51
+ messages,
52
+ });
53
+ let responseData;
54
+ try {
55
+ const controller = new AbortController();
56
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
57
+ const response = await fetch(this.apiUrl, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'content-type': 'application/json',
61
+ 'authorization': `Bearer ${this.apiKey}`,
62
+ },
63
+ body,
64
+ signal: controller.signal,
65
+ });
66
+ clearTimeout(timeoutId);
67
+ if (response.status === 429) {
68
+ const retryAfter = response.headers.get('retry-after');
69
+ const waitSeconds = retryAfter ? parseInt(retryAfter, 10) : 5;
70
+ logger.logger.warn(`Perplexity API: rate limited, retrying after ${waitSeconds}s`);
71
+ await sleep(waitSeconds * 1000);
72
+ return this.suggestLocator(request);
73
+ }
74
+ responseData = (await response.json());
75
+ if (!response.ok) {
76
+ const errorResp = responseData;
77
+ throw new Error(`Perplexity API error (${response.status}): ${errorResp.error?.message ?? response.statusText}`);
78
+ }
79
+ }
80
+ catch (error) {
81
+ if (error instanceof DOMException && error.name === 'AbortError') {
82
+ throw new Error(`[MindHeal] Perplexity API request timed out after ${REQUEST_TIMEOUT_MS}ms`);
83
+ }
84
+ if (error instanceof Error && error.message.startsWith('Perplexity API error')) {
85
+ throw new Error(`[MindHeal] ${error.message}`);
86
+ }
87
+ throw new Error(`[MindHeal] Perplexity API request failed: ${error instanceof Error ? error.message : String(error)}`);
88
+ }
89
+ const successResp = responseData;
90
+ const choice = successResp.choices[0];
91
+ if (!choice?.message?.content) {
92
+ throw new Error('[MindHeal] Perplexity API returned empty response');
93
+ }
94
+ const duration = Date.now() - startTime;
95
+ logger.logger.debug('Perplexity API: received response', {
96
+ model: successResp.model,
97
+ duration: `${duration}ms`,
98
+ promptTokens: successResp.usage.prompt_tokens,
99
+ completionTokens: successResp.usage.completion_tokens,
100
+ });
101
+ try {
102
+ return promptTemplates.parseHealingResponse(choice.message.content);
103
+ }
104
+ catch (parseError) {
105
+ logger.logger.error('Perplexity API: failed to parse response', {
106
+ rawText: choice.message.content.slice(0, 500),
107
+ error: parseError instanceof Error ? parseError.message : String(parseError),
108
+ });
109
+ throw parseError;
110
+ }
111
+ }
112
+ }
113
+ function sleep(ms) {
114
+ return new Promise((resolve) => setTimeout(resolve, ms));
115
+ }
116
+
117
+ exports.PerplexityProvider = PerplexityProvider;
118
+ //# sourceMappingURL=perplexity-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perplexity-provider.js","sources":["../../../../src/ai/perplexity-provider.ts"],"sourcesContent":[null],"names":["buildHealingPrompt","logger","parseHealingResponse"],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ 'use strict';
2
+
3
+ const VALID_LOCATOR_TYPES = new Set([
4
+ 'css', 'xpath', 'role', 'text', 'testid', 'label', 'placeholder', 'alttext', 'title',
5
+ ]);
6
+ /**
7
+ * Builds a complete prompt for the AI provider to suggest a healed locator.
8
+ */
9
+ function buildHealingPrompt(request) {
10
+ const systemPrompt = `You are an expert Playwright test engineer specializing in locator strategies.
11
+ Your task is to analyze a broken Playwright locator and suggest a healed replacement based on the current DOM snapshot.
12
+
13
+ LOCATOR BEST PRACTICES (follow in priority order):
14
+ 1. Prefer \`getByRole\` with accessible name — most resilient to DOM changes
15
+ 2. Prefer \`getByTestId\` when a data-testid attribute is present
16
+ 3. Prefer \`getByLabel\` for form inputs with associated labels
17
+ 4. Prefer \`getByPlaceholder\` for inputs with placeholder text
18
+ 5. Prefer \`getByText\` for elements with unique, stable visible text
19
+ 6. Prefer \`getByAltText\` for images with alt attributes
20
+ 7. Prefer \`getByTitle\` when title attribute is the best identifier
21
+ 8. Use CSS selectors only as a last resort
22
+ 9. Avoid XPath unless no other strategy is viable
23
+
24
+ SPECIAL CONTEXTS:
25
+ - MODALS/DIALOGS: If the element is inside a modal, dialog, popup, or overlay (look for \`dialog\`, \`role="dialog"\`, \`role="alertdialog"\`, \`aria-modal="true"\`, or classes like \`.modal\`), scope your locator to the modal context. Use \`page.getByRole('dialog').getByRole(...)\` or similar chaining to avoid matching elements behind the modal.
26
+ - WEB TABLES: If the element is inside a \`<table>\`, consider using:
27
+ - \`page.getByRole('cell', { name: '...' })\` for specific cell content
28
+ - \`page.getByRole('row', { name: '...' }).getByRole('cell')\` for row+cell targeting
29
+ - \`page.locator('table >> tr:nth-child(N) >> td:nth-child(M)')\` for index-based access
30
+ - Column header text can help identify the right column
31
+ - SHADOW DOM: If elements are inside shadow roots (\`#shadow-root\` markers in the snapshot), Playwright can pierce shadow DOM with CSS selectors or via \`page.locator('host-element').locator('inner-selector')\`
32
+ - ENTERPRISE APPLICATIONS: If the DOM contains SAP (ui5-*, sap-*), Salesforce (lightning-*, force-*), ServiceNow (now-*, sn-*), or similar enterprise elements:
33
+ - NEVER rely on dynamically generated IDs (e.g., \`__xmlview0--\`, \`globalId_\`, \`auraId_\`). Use \`[id$="stablePart"]\` suffix matching instead.
34
+ - Prefer \`data-automation-id\`, \`data-testid\`, \`data-field\`, \`data-field-name\`, \`data-component-id\`, or \`data-control-name\` attributes.
35
+ - For SAP UI5, use \`[data-sap-ui]\` or \`[data-sap-ui-id]\` attributes.
36
+ - For Salesforce Lightning, prefer \`lightning-*\` custom element selectors with stable attributes.
37
+ - For ServiceNow, use \`[data-table-name]\`, \`[data-field-name]\`, or \`[data-element]\` attributes.
38
+
39
+ IMPORTANT:
40
+ - The playwrightExpression must be a valid Playwright locator call (e.g., \`page.getByRole('button', { name: 'Submit' })\`)
41
+ - Confidence should be between 0 and 1 (1 = certainty, 0.5 = moderate guess)
42
+ - Provide clear reasoning explaining why you chose this locator
43
+
44
+ You MUST respond with ONLY a JSON object in this exact format (no markdown, no code fences):
45
+ {
46
+ "selector": "<the selector value>",
47
+ "locatorType": "<one of: css, xpath, role, text, testid, label, placeholder, alttext, title>",
48
+ "confidence": <number between 0 and 1>,
49
+ "reasoning": "<brief explanation of why this locator was chosen>",
50
+ "playwrightExpression": "<full Playwright locator expression starting with page.>"
51
+ }`;
52
+ const userPrompt = `A Playwright locator has broken. Please analyze the DOM and suggest a healed locator.
53
+
54
+ BROKEN LOCATOR:
55
+ - Type: ${request.originalLocator.type}
56
+ - Selector: ${request.originalLocator.selector}
57
+ - Playwright expression: ${request.originalLocator.playwrightExpression}
58
+
59
+ ERROR MESSAGE:
60
+ ${request.errorMessage}
61
+
62
+ PAGE URL: ${request.pageUrl}
63
+ ACTION ATTEMPTED: ${request.action}
64
+
65
+ CURRENT DOM SNAPSHOT:
66
+ ${request.domSnapshot}${request.nearbyElements ? `
67
+
68
+ NEARBY ELEMENTS:
69
+ ${request.nearbyElements}` : ''}${request.ragContext && request.ragContext.length > 0 ? `
70
+
71
+ PROJECT CONTEXT (from RAG knowledge base):
72
+ ${formatRAGContext(request.ragContext)}
73
+
74
+ Use the above project context to make a more informed decision. For example:
75
+ - If healing history shows a previous fix for a similar locator, prefer that pattern.
76
+ - If page object metadata lists known selectors, use them.
77
+ - If git changes show a recent rename, the new name is likely correct.
78
+ - If component docs describe the expected structure, align with it.` : ''}
79
+
80
+ Respond with ONLY the JSON object — no extra text.`;
81
+ return `${systemPrompt}\n\n---\n\n${userPrompt}`;
82
+ }
83
+ /**
84
+ * Parses the AI provider's raw text response into a structured AIHealingResponse.
85
+ * Handles malformed responses gracefully by attempting multiple extraction strategies.
86
+ */
87
+ function parseHealingResponse(response) {
88
+ const trimmed = response.trim();
89
+ // Strategy 1: Try direct JSON parse
90
+ const directParsed = tryParseJSON(trimmed);
91
+ if (directParsed) {
92
+ return validateAndNormalize(directParsed);
93
+ }
94
+ // Strategy 2: Extract JSON from markdown code fences
95
+ const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
96
+ if (fenceMatch) {
97
+ const fenceParsed = tryParseJSON(fenceMatch[1].trim());
98
+ if (fenceParsed) {
99
+ return validateAndNormalize(fenceParsed);
100
+ }
101
+ }
102
+ // Strategy 3: Find first { ... } block in the response
103
+ const braceStart = trimmed.indexOf('{');
104
+ const braceEnd = trimmed.lastIndexOf('}');
105
+ if (braceStart !== -1 && braceEnd > braceStart) {
106
+ const extracted = trimmed.slice(braceStart, braceEnd + 1);
107
+ const extractedParsed = tryParseJSON(extracted);
108
+ if (extractedParsed) {
109
+ return validateAndNormalize(extractedParsed);
110
+ }
111
+ }
112
+ throw new Error(`[MindHeal] Failed to parse AI healing response. Raw response: ${trimmed.slice(0, 500)}`);
113
+ }
114
+ function tryParseJSON(text) {
115
+ try {
116
+ const parsed = JSON.parse(text);
117
+ if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
118
+ return parsed;
119
+ }
120
+ return null;
121
+ }
122
+ catch {
123
+ return null;
124
+ }
125
+ }
126
+ function validateAndNormalize(data) {
127
+ const selector = extractString(data, 'selector');
128
+ if (!selector) {
129
+ throw new Error('[MindHeal] AI response missing required field: selector');
130
+ }
131
+ const playwrightExpression = extractString(data, 'playwrightExpression');
132
+ if (!playwrightExpression) {
133
+ throw new Error('[MindHeal] AI response missing required field: playwrightExpression');
134
+ }
135
+ const rawLocatorType = extractString(data, 'locatorType') ?? 'css';
136
+ const locatorType = VALID_LOCATOR_TYPES.has(rawLocatorType)
137
+ ? rawLocatorType
138
+ : 'css';
139
+ const rawConfidence = typeof data['confidence'] === 'number' ? data['confidence'] : 0.5;
140
+ const confidence = Math.max(0, Math.min(1, rawConfidence));
141
+ const reasoning = extractString(data, 'reasoning') ?? 'No reasoning provided by AI';
142
+ return {
143
+ selector,
144
+ locatorType,
145
+ confidence,
146
+ reasoning,
147
+ playwrightExpression,
148
+ };
149
+ }
150
+ function extractString(data, key) {
151
+ const value = data[key];
152
+ if (typeof value === 'string' && value.length > 0) {
153
+ return value;
154
+ }
155
+ return undefined;
156
+ }
157
+ /**
158
+ * Formats RAG context chunks into a readable section for the AI prompt.
159
+ */
160
+ function formatRAGContext(chunks) {
161
+ return chunks
162
+ .map((chunk, i) => {
163
+ const header = `[${i + 1}] Source: ${chunk.source} (relevance: ${chunk.relevanceScore.toFixed(2)})`;
164
+ const meta = Object.entries(chunk.metadata)
165
+ .map(([k, v]) => ` ${k}: ${v}`)
166
+ .join('\n');
167
+ return `${header}\n${meta ? meta + '\n' : ''}${chunk.content}`;
168
+ })
169
+ .join('\n\n---\n\n');
170
+ }
171
+
172
+ exports.buildHealingPrompt = buildHealingPrompt;
173
+ exports.parseHealingResponse = parseHealingResponse;
174
+ //# sourceMappingURL=prompt-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-templates.js","sources":["../../../../src/ai/prompt-templates.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAEA,MAAM,mBAAmB,GAA6B,IAAI,GAAG,CAAC;AAC5D,IAAA,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO;AACrF,CAAA,CAAC;AAEF;;AAEG;AACG,SAAU,kBAAkB,CAAC,OAAyB,EAAA;AAC1D,IAAA,MAAM,YAAY,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCrB;AAEA,IAAA,MAAM,UAAU,GAAG,CAAA;;;UAGX,OAAO,CAAC,eAAe,CAAC,IAAI;cACxB,OAAO,CAAC,eAAe,CAAC,QAAQ;2BACnB,OAAO,CAAC,eAAe,CAAC,oBAAoB;;;AAGrE,EAAA,OAAO,CAAC,YAAY;;AAEV,UAAA,EAAA,OAAO,CAAC,OAAO;AACP,kBAAA,EAAA,OAAO,CAAC,MAAM;;;EAGhC,OAAO,CAAC,WAAW,CAAA,EAAG,OAAO,CAAC,cAAc,GAAG;;;EAG/C,OAAO,CAAC,cAAc,CAAA,CAAE,GAAG,EAAE,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,GAAG;;;AAGtF,EAAA,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC;;;;;;oEAM8B,GAAG,EAAE;;mDAEtB;AAEjD,IAAA,OAAO,CAAA,EAAG,YAAY,CAAA,WAAA,EAAc,UAAU,EAAE;AAClD;AAEA;;;AAGG;AACG,SAAU,oBAAoB,CAAC,QAAgB,EAAA;AACnD,IAAA,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE;;AAG/B,IAAA,MAAM,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC;IAC1C,IAAI,YAAY,EAAE;AAChB,QAAA,OAAO,oBAAoB,CAAC,YAAY,CAAC;IAC3C;;IAGA,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC;IACzE,IAAI,UAAU,EAAE;AACd,QAAA,MAAM,WAAW,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtD,IAAI,WAAW,EAAE;AACf,YAAA,OAAO,oBAAoB,CAAC,WAAW,CAAC;QAC1C;IACF;;IAGA,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC;IACzC,IAAI,UAAU,KAAK,EAAE,IAAI,QAAQ,GAAG,UAAU,EAAE;AAC9C,QAAA,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,GAAG,CAAC,CAAC;AACzD,QAAA,MAAM,eAAe,GAAG,YAAY,CAAC,SAAS,CAAC;QAC/C,IAAI,eAAe,EAAE;AACnB,YAAA,OAAO,oBAAoB,CAAC,eAAe,CAAC;QAC9C;IACF;AAEA,IAAA,MAAM,IAAI,KAAK,CACb,CAAA,8DAAA,EAAiE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA,CAAE,CACzF;AACH;AAEA,SAAS,YAAY,CAAC,IAAY,EAAA;AAChC,IAAA,IAAI;QACF,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;AACxC,QAAA,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AAC3E,YAAA,OAAO,MAAiC;QAC1C;AACA,QAAA,OAAO,IAAI;IACb;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,IAAI;IACb;AACF;AAEA,SAAS,oBAAoB,CAAC,IAA6B,EAAA;IACzD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC;IAChD,IAAI,CAAC,QAAQ,EAAE;AACb,QAAA,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC;IAC5E;IAEA,MAAM,oBAAoB,GAAG,aAAa,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACxE,IAAI,CAAC,oBAAoB,EAAE;AACzB,QAAA,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC;IACxF;IAEA,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,KAAK;AAClE,IAAA,MAAM,WAAW,GAAgB,mBAAmB,CAAC,GAAG,CAAC,cAA6B;AACpF,UAAG;UACD,KAAK;IAET,MAAM,aAAa,GAAG,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,GAAG;AACvF,IAAA,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IAE1D,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,6BAA6B;IAEnF,OAAO;QACL,QAAQ;QACR,WAAW;QACX,UAAU;QACV,SAAS;QACT,oBAAoB;KACrB;AACH;AAEA,SAAS,aAAa,CAAC,IAA6B,EAAE,GAAW,EAAA;AAC/D,IAAA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC;IACvB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;AACjD,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAO,SAAS;AAClB;AAEA;;AAEG;AACH,SAAS,gBAAgB,CAAC,MAAyB,EAAA;AACjD,IAAA,OAAO;AACJ,SAAA,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,KAAI;QAChB,MAAM,MAAM,GAAG,CAAA,CAAA,EAAI,CAAC,GAAG,CAAC,CAAA,UAAA,EAAa,KAAK,CAAC,MAAM,gBAAgB,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QACnG,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ;AACvC,aAAA,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAA,EAAA,EAAK,CAAC,CAAA,EAAA,EAAK,CAAC,EAAE;aAC9B,IAAI,CAAC,IAAI,CAAC;AACb,QAAA,OAAO,GAAG,MAAM,CAAA,EAAA,EAAK,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,EAAE,CAAA,EAAG,KAAK,CAAC,OAAO,EAAE;AAChE,IAAA,CAAC;SACA,IAAI,CAAC,aAAa,CAAC;AACxB;;;;;"}
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ var promptTemplates = require('./prompt-templates.js');
4
+ var logger = require('../utils/logger.js');
5
+
6
+ const DEFAULT_MODEL = 'qwen-plus';
7
+ const DEFAULT_MAX_TOKENS = 1024;
8
+ const DEFAULT_TEMPERATURE = 0;
9
+ const DEFAULT_API_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions';
10
+ const REQUEST_TIMEOUT_MS = 30000;
11
+ class QwenProvider {
12
+ constructor(config) {
13
+ this.name = 'qwen';
14
+ if (!config.apiKey) {
15
+ throw new Error('[MindHeal] Qwen API key is required');
16
+ }
17
+ this.apiKey = config.apiKey;
18
+ this.model = config.model ?? DEFAULT_MODEL;
19
+ this.maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;
20
+ this.temperature = config.temperature ?? DEFAULT_TEMPERATURE;
21
+ this.apiUrl = config.baseUrl ?? DEFAULT_API_URL;
22
+ }
23
+ async suggestLocator(request) {
24
+ const fullPrompt = promptTemplates.buildHealingPrompt(request);
25
+ const startTime = Date.now();
26
+ logger.logger.debug('Qwen API: sending healing request', {
27
+ model: this.model,
28
+ originalLocator: request.originalLocator.selector,
29
+ pageUrl: request.pageUrl,
30
+ });
31
+ // Split the combined prompt into system + user messages at the separator
32
+ const separatorIndex = fullPrompt.indexOf('\n\n---\n\n');
33
+ let systemContent;
34
+ let userContent;
35
+ if (separatorIndex !== -1) {
36
+ systemContent = fullPrompt.slice(0, separatorIndex);
37
+ userContent = fullPrompt.slice(separatorIndex + 7); // length of '\n\n---\n\n'
38
+ }
39
+ else {
40
+ systemContent = 'You are an expert Playwright test engineer. Respond with only valid JSON.';
41
+ userContent = fullPrompt;
42
+ }
43
+ const messages = [
44
+ { role: 'system', content: systemContent },
45
+ { role: 'user', content: userContent },
46
+ ];
47
+ const body = JSON.stringify({
48
+ model: this.model,
49
+ max_tokens: this.maxTokens,
50
+ temperature: this.temperature,
51
+ messages,
52
+ });
53
+ let responseData;
54
+ try {
55
+ const controller = new AbortController();
56
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
57
+ const response = await fetch(this.apiUrl, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'content-type': 'application/json',
61
+ 'authorization': `Bearer ${this.apiKey}`,
62
+ },
63
+ body,
64
+ signal: controller.signal,
65
+ });
66
+ clearTimeout(timeoutId);
67
+ if (response.status === 429) {
68
+ const retryAfter = response.headers.get('retry-after');
69
+ const waitSeconds = retryAfter ? parseInt(retryAfter, 10) : 5;
70
+ logger.logger.warn(`Qwen API: rate limited, retrying after ${waitSeconds}s`);
71
+ await sleep(waitSeconds * 1000);
72
+ return this.suggestLocator(request);
73
+ }
74
+ responseData = (await response.json());
75
+ if (!response.ok) {
76
+ const errorResp = responseData;
77
+ throw new Error(`Qwen API error (${response.status}): ${errorResp.error?.message ?? response.statusText}`);
78
+ }
79
+ }
80
+ catch (error) {
81
+ if (error instanceof DOMException && error.name === 'AbortError') {
82
+ throw new Error(`[MindHeal] Qwen API request timed out after ${REQUEST_TIMEOUT_MS}ms`);
83
+ }
84
+ if (error instanceof Error && error.message.startsWith('Qwen API error')) {
85
+ throw new Error(`[MindHeal] ${error.message}`);
86
+ }
87
+ throw new Error(`[MindHeal] Qwen API request failed: ${error instanceof Error ? error.message : String(error)}`);
88
+ }
89
+ const successResp = responseData;
90
+ const choice = successResp.choices[0];
91
+ if (!choice?.message?.content) {
92
+ throw new Error('[MindHeal] Qwen API returned empty response');
93
+ }
94
+ const duration = Date.now() - startTime;
95
+ logger.logger.debug('Qwen API: received response', {
96
+ model: successResp.model,
97
+ duration: `${duration}ms`,
98
+ promptTokens: successResp.usage.prompt_tokens,
99
+ completionTokens: successResp.usage.completion_tokens,
100
+ });
101
+ try {
102
+ return promptTemplates.parseHealingResponse(choice.message.content);
103
+ }
104
+ catch (parseError) {
105
+ logger.logger.error('Qwen API: failed to parse response', {
106
+ rawText: choice.message.content.slice(0, 500),
107
+ error: parseError instanceof Error ? parseError.message : String(parseError),
108
+ });
109
+ throw parseError;
110
+ }
111
+ }
112
+ }
113
+ function sleep(ms) {
114
+ return new Promise((resolve) => setTimeout(resolve, ms));
115
+ }
116
+
117
+ exports.QwenProvider = QwenProvider;
118
+ //# sourceMappingURL=qwen-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qwen-provider.js","sources":["../../../../src/ai/qwen-provider.ts"],"sourcesContent":[null],"names":["buildHealingPrompt","logger","parseHealingResponse"],"mappings":"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"}