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,136 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+ var logger = require('./logger.js');
6
+
7
+ /**
8
+ * Parallel Execution Safety — File Locking
9
+ *
10
+ * Provides advisory file locking for shared resources (cache, knowledge store,
11
+ * analytics) when Playwright tests run with multiple workers or shards.
12
+ *
13
+ * Uses .lock files with PID + timestamp to detect stale locks.
14
+ * Cross-platform (Windows + macOS + Linux).
15
+ */
16
+ class FileLock {
17
+ constructor(config) {
18
+ this.config = config;
19
+ }
20
+ /**
21
+ * Acquires an advisory lock on the given file path.
22
+ * Returns a release function that MUST be called when done.
23
+ *
24
+ * @param filePath - The file to lock (a .lock file is created alongside it)
25
+ * @returns A function to release the lock
26
+ * @throws If the lock cannot be acquired within the timeout
27
+ */
28
+ async acquire(filePath) {
29
+ if (!this.config.enabled) {
30
+ return () => { }; // No-op release
31
+ }
32
+ const lockPath = `${filePath}.lock`;
33
+ const startTime = Date.now();
34
+ const workerId = `${process.pid}-${Math.random().toString(36).substring(2, 8)}`;
35
+ while (Date.now() - startTime < this.config.lockTimeout) {
36
+ // Check for existing lock
37
+ if (fs.existsSync(lockPath)) {
38
+ const isStale = this.isLockStale(lockPath);
39
+ if (isStale) {
40
+ logger.logger.debug(`[FileLock] Removing stale lock: ${lockPath}`);
41
+ this.removeLock(lockPath);
42
+ }
43
+ else {
44
+ // Wait and retry
45
+ await new Promise((resolve) => setTimeout(resolve, this.config.lockRetryInterval));
46
+ continue;
47
+ }
48
+ }
49
+ // Try to create the lock
50
+ try {
51
+ const dir = path.dirname(lockPath);
52
+ if (!fs.existsSync(dir)) {
53
+ fs.mkdirSync(dir, { recursive: true });
54
+ }
55
+ const lockInfo = {
56
+ pid: process.pid,
57
+ timestamp: Date.now(),
58
+ workerId,
59
+ };
60
+ // Write lock file atomically-ish (best effort)
61
+ fs.writeFileSync(lockPath, JSON.stringify(lockInfo), { flag: 'wx' });
62
+ logger.logger.debug(`[FileLock] Acquired lock: ${lockPath} (worker: ${workerId})`);
63
+ // Return release function
64
+ return () => {
65
+ this.removeLock(lockPath);
66
+ logger.logger.debug(`[FileLock] Released lock: ${lockPath}`);
67
+ };
68
+ }
69
+ catch (err) {
70
+ // File already exists (another process beat us) — retry
71
+ if (err.code === 'EEXIST') {
72
+ await new Promise((resolve) => setTimeout(resolve, this.config.lockRetryInterval));
73
+ continue;
74
+ }
75
+ throw err;
76
+ }
77
+ }
78
+ throw new Error(`[MindHeal] Could not acquire file lock for ${filePath} within ${this.config.lockTimeout}ms`);
79
+ }
80
+ /**
81
+ * Executes a function while holding a file lock.
82
+ * Automatically acquires and releases the lock.
83
+ */
84
+ async withLock(filePath, fn) {
85
+ if (!this.config.enabled) {
86
+ return fn();
87
+ }
88
+ const release = await this.acquire(filePath);
89
+ try {
90
+ return await fn();
91
+ }
92
+ finally {
93
+ release();
94
+ }
95
+ }
96
+ /**
97
+ * Checks if a lock file is stale (process dead or too old).
98
+ */
99
+ isLockStale(lockPath) {
100
+ try {
101
+ const content = fs.readFileSync(lockPath, 'utf-8');
102
+ const info = JSON.parse(content);
103
+ // Check timestamp
104
+ if (Date.now() - info.timestamp > this.config.staleLockThreshold) {
105
+ return true;
106
+ }
107
+ // Check if PID is still alive
108
+ try {
109
+ process.kill(info.pid, 0); // Signal 0 = check if process exists
110
+ return false; // Process is alive
111
+ }
112
+ catch {
113
+ return true; // Process is dead
114
+ }
115
+ }
116
+ catch {
117
+ return true; // Can't read lock — treat as stale
118
+ }
119
+ }
120
+ /**
121
+ * Safely removes a lock file.
122
+ */
123
+ removeLock(lockPath) {
124
+ try {
125
+ if (fs.existsSync(lockPath)) {
126
+ fs.unlinkSync(lockPath);
127
+ }
128
+ }
129
+ catch {
130
+ // Ignore — another process may have removed it
131
+ }
132
+ }
133
+ }
134
+
135
+ exports.FileLock = FileLock;
136
+ //# sourceMappingURL=file-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.js","sources":["../../../../src/utils/file-lock.ts"],"sourcesContent":[null],"names":["existsSync","logger","dirname","mkdirSync","writeFileSync","readFileSync","unlinkSync"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+ var logger = require('./logger.js');
6
+
7
+ function writeFileContent(filePath, content) {
8
+ try {
9
+ const dir = path.dirname(filePath);
10
+ if (!fs.existsSync(dir)) {
11
+ fs.mkdirSync(dir, { recursive: true });
12
+ }
13
+ fs.writeFileSync(filePath, content, 'utf-8');
14
+ }
15
+ catch (error) {
16
+ logger.logger.error(`Failed to write file: ${filePath}`, error);
17
+ throw new Error(`[MindHeal] Cannot write file: ${filePath}`);
18
+ }
19
+ }
20
+ function fileExists(filePath) {
21
+ return fs.existsSync(filePath);
22
+ }
23
+ function ensureDirectory(dirPath) {
24
+ if (!fs.existsSync(dirPath)) {
25
+ fs.mkdirSync(dirPath, { recursive: true });
26
+ }
27
+ }
28
+ function readJsonFile(filePath) {
29
+ try {
30
+ if (!fs.existsSync(filePath))
31
+ return null;
32
+ const content = fs.readFileSync(filePath, 'utf-8');
33
+ return JSON.parse(content);
34
+ }
35
+ catch (error) {
36
+ logger.logger.warn(`Failed to parse JSON file: ${filePath}`, error);
37
+ return null;
38
+ }
39
+ }
40
+ function writeJsonFile(filePath, data) {
41
+ writeFileContent(filePath, JSON.stringify(data, null, 2));
42
+ }
43
+
44
+ exports.ensureDirectory = ensureDirectory;
45
+ exports.fileExists = fileExists;
46
+ exports.readJsonFile = readJsonFile;
47
+ exports.writeFileContent = writeFileContent;
48
+ exports.writeJsonFile = writeJsonFile;
49
+ //# sourceMappingURL=file-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-utils.js","sources":["../../../../src/utils/file-utils.ts"],"sourcesContent":[null],"names":["dirname","existsSync","mkdirSync","writeFileSync","logger","readFileSync"],"mappings":";;;;;;AAaM,SAAU,gBAAgB,CAAC,QAAgB,EAAE,OAAe,EAAA;AAChE,IAAA,IAAI;AACF,QAAA,MAAM,GAAG,GAAGA,YAAO,CAAC,QAAQ,CAAC;AAC7B,QAAA,IAAI,CAACC,aAAU,CAAC,GAAG,CAAC,EAAE;YACpBC,YAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACrC;AACA,QAAAC,gBAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC;IAC3C;IAAE,OAAO,KAAK,EAAE;QACdC,aAAM,CAAC,KAAK,CAAC,CAAA,sBAAA,EAAyB,QAAQ,CAAA,CAAE,EAAE,KAAK,CAAC;AACxD,QAAA,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,CAAA,CAAE,CAAC;IAC9D;AACF;AAEM,SAAU,UAAU,CAAC,QAAgB,EAAA;AACzC,IAAA,OAAOH,aAAU,CAAC,QAAQ,CAAC;AAC7B;AAEM,SAAU,eAAe,CAAC,OAAe,EAAA;AAC7C,IAAA,IAAI,CAACA,aAAU,CAAC,OAAO,CAAC,EAAE;QACxBC,YAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACzC;AACF;AAEM,SAAU,YAAY,CAAI,QAAgB,EAAA;AAC9C,IAAA,IAAI;AACF,QAAA,IAAI,CAACD,aAAU,CAAC,QAAQ,CAAC;AAAE,YAAA,OAAO,IAAI;QACtC,MAAM,OAAO,GAAGI,eAAY,CAAC,QAAQ,EAAE,OAAO,CAAC;AAC/C,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM;IACjC;IAAE,OAAO,KAAK,EAAE;QACdD,aAAM,CAAC,IAAI,CAAC,CAAA,2BAAA,EAA8B,QAAQ,CAAA,CAAE,EAAE,KAAK,CAAC;AAC5D,QAAA,OAAO,IAAI;IACb;AACF;AAEM,SAAU,aAAa,CAAC,QAAgB,EAAE,IAAa,EAAA;AAC3D,IAAA,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC3D;;;;;;;;"}
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
7
+ const PREFIX = '[MindHeal]';
8
+ let currentLevel = 'info';
9
+ let logFilePath;
10
+ function configureLogger(config) {
11
+ currentLevel = config.level;
12
+ logFilePath = config.file;
13
+ if (logFilePath) {
14
+ const dir = path.dirname(logFilePath);
15
+ if (!fs.existsSync(dir)) {
16
+ fs.mkdirSync(dir, { recursive: true });
17
+ }
18
+ }
19
+ }
20
+ function shouldLog(level) {
21
+ return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];
22
+ }
23
+ function formatMessage(level, message, data) {
24
+ const timestamp = new Date().toISOString();
25
+ const base = `${timestamp} ${PREFIX} [${level.toUpperCase()}] ${message}`;
26
+ if (data !== undefined) {
27
+ return `${base} ${JSON.stringify(data, null, 2)}`;
28
+ }
29
+ return base;
30
+ }
31
+ function writeToFile(formatted) {
32
+ if (logFilePath) {
33
+ try {
34
+ fs.appendFileSync(logFilePath, formatted + '\n', 'utf-8');
35
+ }
36
+ catch {
37
+ // Silently fail file logging
38
+ }
39
+ }
40
+ }
41
+ const logger = {
42
+ debug(message, data) {
43
+ if (!shouldLog('debug'))
44
+ return;
45
+ const formatted = formatMessage('debug', message, data);
46
+ writeToFile(formatted);
47
+ // eslint-disable-next-line no-console
48
+ console.debug(formatted);
49
+ },
50
+ info(message, data) {
51
+ if (!shouldLog('info'))
52
+ return;
53
+ const formatted = formatMessage('info', message, data);
54
+ writeToFile(formatted);
55
+ // eslint-disable-next-line no-console
56
+ console.info(formatted);
57
+ },
58
+ warn(message, data) {
59
+ if (!shouldLog('warn'))
60
+ return;
61
+ const formatted = formatMessage('warn', message, data);
62
+ writeToFile(formatted);
63
+ // eslint-disable-next-line no-console
64
+ console.warn(formatted);
65
+ },
66
+ error(message, data) {
67
+ if (!shouldLog('error'))
68
+ return;
69
+ const formatted = formatMessage('error', message, data);
70
+ writeToFile(formatted);
71
+ // eslint-disable-next-line no-console
72
+ console.error(formatted);
73
+ },
74
+ };
75
+
76
+ exports.configureLogger = configureLogger;
77
+ exports.logger = logger;
78
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sources":["../../../../src/utils/logger.ts"],"sourcesContent":[null],"names":["dirname","existsSync","mkdirSync","appendFileSync"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { AnthropicProvider } from './anthropic-provider.js';
2
+ import { OpenAIProvider } from './openai-provider.js';
3
+ import { AzureOpenAIProvider } from './azure-openai-provider.js';
4
+ import { GeminiProvider } from './gemini-provider.js';
5
+ import { OllamaProvider } from './ollama-provider.js';
6
+ import { BedrockProvider } from './bedrock-provider.js';
7
+ import { DeepSeekProvider } from './deepseek-provider.js';
8
+ import { GroqProvider } from './groq-provider.js';
9
+ import { QwenProvider } from './qwen-provider.js';
10
+ import { MetaProvider } from './meta-provider.js';
11
+ import { PerplexityProvider } from './perplexity-provider.js';
12
+
13
+ function createAIProvider(config) {
14
+ switch (config.provider) {
15
+ case 'anthropic':
16
+ return new AnthropicProvider(config);
17
+ case 'openai':
18
+ return new OpenAIProvider(config);
19
+ case 'azure-openai':
20
+ return new AzureOpenAIProvider(config);
21
+ case 'gemini':
22
+ return new GeminiProvider(config);
23
+ case 'ollama':
24
+ return new OllamaProvider(config);
25
+ case 'aws-bedrock':
26
+ return new BedrockProvider(config);
27
+ case 'deepseek':
28
+ return new DeepSeekProvider(config);
29
+ case 'groq':
30
+ return new GroqProvider(config);
31
+ case 'qwen':
32
+ return new QwenProvider(config);
33
+ case 'meta':
34
+ return new MetaProvider(config);
35
+ case 'perplexity':
36
+ return new PerplexityProvider(config);
37
+ default:
38
+ throw new Error(`[MindHeal] Unsupported AI provider: "${config.provider}". ` +
39
+ `Supported: anthropic, openai, azure-openai, gemini, ollama, aws-bedrock, deepseek, groq, qwen, meta, perplexity`);
40
+ }
41
+ }
42
+
43
+ export { createAIProvider };
44
+ //# sourceMappingURL=ai-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-provider.js","sources":["../../../../src/ai/ai-provider.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;AAaM,SAAU,gBAAgB,CAAC,MAAgB,EAAA;AAC/C,IAAA,QAAQ,MAAM,CAAC,QAAQ;AACrB,QAAA,KAAK,WAAW;AACd,YAAA,OAAO,IAAI,iBAAiB,CAAC,MAAM,CAAC;AACtC,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC;AACnC,QAAA,KAAK,cAAc;AACjB,YAAA,OAAO,IAAI,mBAAmB,CAAC,MAAM,CAAC;AACxC,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC;AACnC,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC;AACnC,QAAA,KAAK,aAAa;AAChB,YAAA,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC;AACpC,QAAA,KAAK,UAAU;AACb,YAAA,OAAO,IAAI,gBAAgB,CAAC,MAAM,CAAC;AACrC,QAAA,KAAK,MAAM;AACT,YAAA,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC;AACjC,QAAA,KAAK,MAAM;AACT,YAAA,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC;AACjC,QAAA,KAAK,MAAM;AACT,YAAA,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC;AACjC,QAAA,KAAK,YAAY;AACf,YAAA,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC;AACvC,QAAA;AACE,YAAA,MAAM,IAAI,KAAK,CACb,wCAAwC,MAAM,CAAC,QAAQ,CAAA,GAAA,CAAK;AAC5D,gBAAA,CAAA,+GAAA,CAAiH,CAClH;;AAEP;;;;"}
@@ -0,0 +1,104 @@
1
+ import { buildHealingPrompt, parseHealingResponse } from './prompt-templates.js';
2
+ import { logger } from '../utils/logger.js';
3
+
4
+ const DEFAULT_MODEL = 'claude-sonnet-4-20250514';
5
+ const DEFAULT_MAX_TOKENS = 1024;
6
+ const DEFAULT_TEMPERATURE = 0;
7
+ const API_URL = 'https://api.anthropic.com/v1/messages';
8
+ const ANTHROPIC_VERSION = '2023-06-01';
9
+ const REQUEST_TIMEOUT_MS = 30000;
10
+ class AnthropicProvider {
11
+ constructor(config) {
12
+ this.name = 'anthropic';
13
+ if (!config.apiKey) {
14
+ throw new Error('[MindHeal] Anthropic API key is required');
15
+ }
16
+ this.apiKey = config.apiKey;
17
+ this.model = config.model ?? DEFAULT_MODEL;
18
+ this.maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;
19
+ this.temperature = config.temperature ?? DEFAULT_TEMPERATURE;
20
+ }
21
+ async suggestLocator(request) {
22
+ const prompt = buildHealingPrompt(request);
23
+ const startTime = Date.now();
24
+ logger.debug('Anthropic API: sending healing request', {
25
+ model: this.model,
26
+ originalLocator: request.originalLocator.selector,
27
+ pageUrl: request.pageUrl,
28
+ });
29
+ const messages = [
30
+ { role: 'user', content: prompt },
31
+ ];
32
+ const body = JSON.stringify({
33
+ model: this.model,
34
+ max_tokens: this.maxTokens,
35
+ temperature: this.temperature,
36
+ messages,
37
+ });
38
+ let responseData;
39
+ try {
40
+ const controller = new AbortController();
41
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
42
+ const response = await fetch(API_URL, {
43
+ method: 'POST',
44
+ headers: {
45
+ 'content-type': 'application/json',
46
+ 'x-api-key': this.apiKey,
47
+ 'anthropic-version': ANTHROPIC_VERSION,
48
+ },
49
+ body,
50
+ signal: controller.signal,
51
+ });
52
+ clearTimeout(timeoutId);
53
+ if (response.status === 429) {
54
+ const retryAfter = response.headers.get('retry-after');
55
+ const waitSeconds = retryAfter ? parseInt(retryAfter, 10) : 5;
56
+ logger.warn(`Anthropic API: rate limited, retrying after ${waitSeconds}s`);
57
+ await sleep(waitSeconds * 1000);
58
+ return this.suggestLocator(request);
59
+ }
60
+ responseData = (await response.json());
61
+ if (!response.ok) {
62
+ const errorResp = responseData;
63
+ throw new Error(`Anthropic API error (${response.status}): ${errorResp.error?.message ?? response.statusText}`);
64
+ }
65
+ }
66
+ catch (error) {
67
+ if (error instanceof DOMException && error.name === 'AbortError') {
68
+ throw new Error(`[MindHeal] Anthropic API request timed out after ${REQUEST_TIMEOUT_MS}ms`);
69
+ }
70
+ if (error instanceof Error && error.message.startsWith('Anthropic API error')) {
71
+ throw new Error(`[MindHeal] ${error.message}`);
72
+ }
73
+ throw new Error(`[MindHeal] Anthropic API request failed: ${error instanceof Error ? error.message : String(error)}`);
74
+ }
75
+ const successResp = responseData;
76
+ const textContent = successResp.content.find((c) => c.type === 'text');
77
+ if (!textContent?.text) {
78
+ throw new Error('[MindHeal] Anthropic API returned empty response');
79
+ }
80
+ const duration = Date.now() - startTime;
81
+ logger.debug('Anthropic API: received response', {
82
+ model: successResp.model,
83
+ duration: `${duration}ms`,
84
+ inputTokens: successResp.usage.input_tokens,
85
+ outputTokens: successResp.usage.output_tokens,
86
+ });
87
+ try {
88
+ return parseHealingResponse(textContent.text);
89
+ }
90
+ catch (parseError) {
91
+ logger.error('Anthropic API: failed to parse response', {
92
+ rawText: textContent.text.slice(0, 500),
93
+ error: parseError instanceof Error ? parseError.message : String(parseError),
94
+ });
95
+ throw parseError;
96
+ }
97
+ }
98
+ }
99
+ function sleep(ms) {
100
+ return new Promise((resolve) => setTimeout(resolve, ms));
101
+ }
102
+
103
+ export { AnthropicProvider };
104
+ //# sourceMappingURL=anthropic-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic-provider.js","sources":["../../../../src/ai/anthropic-provider.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import { buildHealingPrompt, parseHealingResponse } from './prompt-templates.js';
2
+ import { logger } from '../utils/logger.js';
3
+
4
+ const DEFAULT_API_VERSION = '2024-02-01';
5
+ const DEFAULT_MAX_TOKENS = 1024;
6
+ const DEFAULT_TEMPERATURE = 0;
7
+ const REQUEST_TIMEOUT_MS = 30000;
8
+ class AzureOpenAIProvider {
9
+ constructor(config) {
10
+ this.name = 'azure-openai';
11
+ if (!config.apiKey) {
12
+ throw new Error('[MindHeal] Azure OpenAI API key is required');
13
+ }
14
+ if (!config.baseUrl) {
15
+ throw new Error('[MindHeal] Azure OpenAI baseUrl is required (e.g., https://my-resource.openai.azure.com)');
16
+ }
17
+ if (!config.azureDeploymentName) {
18
+ throw new Error('[MindHeal] Azure OpenAI deployment name is required');
19
+ }
20
+ this.apiKey = config.apiKey;
21
+ this.deploymentName = config.azureDeploymentName;
22
+ this.apiVersion = config.azureApiVersion ?? DEFAULT_API_VERSION;
23
+ this.maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;
24
+ this.temperature = config.temperature ?? DEFAULT_TEMPERATURE;
25
+ // Strip trailing slash for consistency
26
+ let baseUrl = config.baseUrl;
27
+ if (baseUrl.endsWith('/')) {
28
+ baseUrl = baseUrl.slice(0, -1);
29
+ }
30
+ this.baseUrl = baseUrl;
31
+ }
32
+ async suggestLocator(request) {
33
+ const fullPrompt = buildHealingPrompt(request);
34
+ const startTime = Date.now();
35
+ logger.debug('Azure OpenAI API: sending healing request', {
36
+ deploymentName: this.deploymentName,
37
+ apiVersion: this.apiVersion,
38
+ originalLocator: request.originalLocator.selector,
39
+ pageUrl: request.pageUrl,
40
+ });
41
+ // Split the combined prompt into system + user messages at the separator
42
+ const separatorIndex = fullPrompt.indexOf('\n\n---\n\n');
43
+ let systemContent;
44
+ let userContent;
45
+ if (separatorIndex !== -1) {
46
+ systemContent = fullPrompt.slice(0, separatorIndex);
47
+ userContent = fullPrompt.slice(separatorIndex + 7); // length of '\n\n---\n\n'
48
+ }
49
+ else {
50
+ systemContent = 'You are an expert Playwright test engineer. Respond with only valid JSON.';
51
+ userContent = fullPrompt;
52
+ }
53
+ const messages = [
54
+ { role: 'system', content: systemContent },
55
+ { role: 'user', content: userContent },
56
+ ];
57
+ const body = JSON.stringify({
58
+ max_tokens: this.maxTokens,
59
+ temperature: this.temperature,
60
+ messages,
61
+ });
62
+ const url = `${this.baseUrl}/openai/deployments/${this.deploymentName}/chat/completions?api-version=${this.apiVersion}`;
63
+ let responseData;
64
+ try {
65
+ const controller = new AbortController();
66
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
67
+ const response = await fetch(url, {
68
+ method: 'POST',
69
+ headers: {
70
+ 'content-type': 'application/json',
71
+ 'api-key': this.apiKey,
72
+ },
73
+ body,
74
+ signal: controller.signal,
75
+ });
76
+ clearTimeout(timeoutId);
77
+ if (response.status === 429) {
78
+ const retryAfter = response.headers.get('retry-after');
79
+ const waitSeconds = retryAfter ? parseInt(retryAfter, 10) : 5;
80
+ logger.warn(`Azure OpenAI API: rate limited, retrying after ${waitSeconds}s`);
81
+ await sleep(waitSeconds * 1000);
82
+ return this.suggestLocator(request);
83
+ }
84
+ responseData = (await response.json());
85
+ if (!response.ok) {
86
+ const errorResp = responseData;
87
+ throw new Error(`Azure OpenAI API error (${response.status}): ${errorResp.error?.message ?? response.statusText}`);
88
+ }
89
+ }
90
+ catch (error) {
91
+ if (error instanceof DOMException && error.name === 'AbortError') {
92
+ throw new Error(`[MindHeal] Azure OpenAI API request timed out after ${REQUEST_TIMEOUT_MS}ms`);
93
+ }
94
+ if (error instanceof Error && error.message.startsWith('Azure OpenAI API error')) {
95
+ throw new Error(`[MindHeal] ${error.message}`);
96
+ }
97
+ throw new Error(`[MindHeal] Azure OpenAI API request failed: ${error instanceof Error ? error.message : String(error)}`);
98
+ }
99
+ const successResp = responseData;
100
+ const choice = successResp.choices[0];
101
+ if (!choice?.message?.content) {
102
+ throw new Error('[MindHeal] Azure OpenAI API returned empty response');
103
+ }
104
+ const duration = Date.now() - startTime;
105
+ logger.debug('Azure OpenAI API: received response', {
106
+ model: successResp.model,
107
+ duration: `${duration}ms`,
108
+ promptTokens: successResp.usage.prompt_tokens,
109
+ completionTokens: successResp.usage.completion_tokens,
110
+ });
111
+ try {
112
+ return parseHealingResponse(choice.message.content);
113
+ }
114
+ catch (parseError) {
115
+ logger.error('Azure OpenAI API: failed to parse response', {
116
+ rawText: choice.message.content.slice(0, 500),
117
+ error: parseError instanceof Error ? parseError.message : String(parseError),
118
+ });
119
+ throw parseError;
120
+ }
121
+ }
122
+ }
123
+ function sleep(ms) {
124
+ return new Promise((resolve) => setTimeout(resolve, ms));
125
+ }
126
+
127
+ export { AzureOpenAIProvider };
128
+ //# sourceMappingURL=azure-openai-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azure-openai-provider.js","sources":["../../../../src/ai/azure-openai-provider.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}