playwright-locator-healer 0.1.3

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 (137) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/LICENSE +21 -0
  3. package/README.md +338 -0
  4. package/USAGE.md +217 -0
  5. package/dist/healing/audit-log.d.ts +9 -0
  6. package/dist/healing/audit-log.d.ts.map +1 -0
  7. package/dist/healing/audit-log.js +22 -0
  8. package/dist/healing/audit-log.js.map +1 -0
  9. package/dist/healing/auto-fixture.d.ts +14 -0
  10. package/dist/healing/auto-fixture.d.ts.map +1 -0
  11. package/dist/healing/auto-fixture.js +430 -0
  12. package/dist/healing/auto-fixture.js.map +1 -0
  13. package/dist/healing/cache.d.ts +11 -0
  14. package/dist/healing/cache.d.ts.map +1 -0
  15. package/dist/healing/cache.js +128 -0
  16. package/dist/healing/cache.js.map +1 -0
  17. package/dist/healing/circuit-breaker.d.ts +11 -0
  18. package/dist/healing/circuit-breaker.d.ts.map +1 -0
  19. package/dist/healing/circuit-breaker.js +34 -0
  20. package/dist/healing/circuit-breaker.js.map +1 -0
  21. package/dist/healing/cost-tracker.d.ts +30 -0
  22. package/dist/healing/cost-tracker.d.ts.map +1 -0
  23. package/dist/healing/cost-tracker.js +101 -0
  24. package/dist/healing/cost-tracker.js.map +1 -0
  25. package/dist/healing/dom-extractor.d.ts +9 -0
  26. package/dist/healing/dom-extractor.d.ts.map +1 -0
  27. package/dist/healing/dom-extractor.js +268 -0
  28. package/dist/healing/dom-extractor.js.map +1 -0
  29. package/dist/healing/errors.d.ts +33 -0
  30. package/dist/healing/errors.d.ts.map +1 -0
  31. package/dist/healing/errors.js +40 -0
  32. package/dist/healing/errors.js.map +1 -0
  33. package/dist/healing/heal/caller-context.d.ts +8 -0
  34. package/dist/healing/heal/caller-context.d.ts.map +1 -0
  35. package/dist/healing/heal/caller-context.js +46 -0
  36. package/dist/healing/heal/caller-context.js.map +1 -0
  37. package/dist/healing/heal/orchestrator-helpers.d.ts +84 -0
  38. package/dist/healing/heal/orchestrator-helpers.d.ts.map +1 -0
  39. package/dist/healing/heal/orchestrator-helpers.js +117 -0
  40. package/dist/healing/heal/orchestrator-helpers.js.map +1 -0
  41. package/dist/healing/heal/orchestrator.d.ts +47 -0
  42. package/dist/healing/heal/orchestrator.d.ts.map +1 -0
  43. package/dist/healing/heal/orchestrator.js +644 -0
  44. package/dist/healing/heal/orchestrator.js.map +1 -0
  45. package/dist/healing/heal/playwright-code.d.ts +26 -0
  46. package/dist/healing/heal/playwright-code.d.ts.map +1 -0
  47. package/dist/healing/heal/playwright-code.js +134 -0
  48. package/dist/healing/heal/playwright-code.js.map +1 -0
  49. package/dist/healing/heal/preflight.d.ts +8 -0
  50. package/dist/healing/heal/preflight.d.ts.map +1 -0
  51. package/dist/healing/heal/preflight.js +77 -0
  52. package/dist/healing/heal/preflight.js.map +1 -0
  53. package/dist/healing/heal/prompt.d.ts +9 -0
  54. package/dist/healing/heal/prompt.d.ts.map +1 -0
  55. package/dist/healing/heal/prompt.js +22 -0
  56. package/dist/healing/heal/prompt.js.map +1 -0
  57. package/dist/healing/heal/report.d.ts +11 -0
  58. package/dist/healing/heal/report.d.ts.map +1 -0
  59. package/dist/healing/heal/report.js +28 -0
  60. package/dist/healing/heal/report.js.map +1 -0
  61. package/dist/healing/heal/source-trace.d.ts +17 -0
  62. package/dist/healing/heal/source-trace.d.ts.map +1 -0
  63. package/dist/healing/heal/source-trace.js +59 -0
  64. package/dist/healing/heal/source-trace.js.map +1 -0
  65. package/dist/healing/index.d.ts +6 -0
  66. package/dist/healing/index.d.ts.map +1 -0
  67. package/dist/healing/index.js +3 -0
  68. package/dist/healing/index.js.map +1 -0
  69. package/dist/healing/llm-client.d.ts +58 -0
  70. package/dist/healing/llm-client.d.ts.map +1 -0
  71. package/dist/healing/llm-client.js +258 -0
  72. package/dist/healing/llm-client.js.map +1 -0
  73. package/dist/healing/logger.d.ts +18 -0
  74. package/dist/healing/logger.d.ts.map +1 -0
  75. package/dist/healing/logger.js +38 -0
  76. package/dist/healing/logger.js.map +1 -0
  77. package/dist/healing/models.d.ts +26 -0
  78. package/dist/healing/models.d.ts.map +1 -0
  79. package/dist/healing/models.js +109 -0
  80. package/dist/healing/models.js.map +1 -0
  81. package/dist/healing/overlay/bridge.d.ts +46 -0
  82. package/dist/healing/overlay/bridge.d.ts.map +1 -0
  83. package/dist/healing/overlay/bridge.js +2 -0
  84. package/dist/healing/overlay/bridge.js.map +1 -0
  85. package/dist/healing/overlay/dock.css.d.ts +2 -0
  86. package/dist/healing/overlay/dock.css.d.ts.map +1 -0
  87. package/dist/healing/overlay/dock.css.js +448 -0
  88. package/dist/healing/overlay/dock.css.js.map +1 -0
  89. package/dist/healing/overlay/dock.html.d.ts +46 -0
  90. package/dist/healing/overlay/dock.html.d.ts.map +1 -0
  91. package/dist/healing/overlay/dock.html.js +248 -0
  92. package/dist/healing/overlay/dock.html.js.map +1 -0
  93. package/dist/healing/overlay/drag.d.ts +17 -0
  94. package/dist/healing/overlay/drag.d.ts.map +1 -0
  95. package/dist/healing/overlay/drag.js +68 -0
  96. package/dist/healing/overlay/drag.js.map +1 -0
  97. package/dist/healing/overlay/inject.d.ts +41 -0
  98. package/dist/healing/overlay/inject.d.ts.map +1 -0
  99. package/dist/healing/overlay/inject.js +277 -0
  100. package/dist/healing/overlay/inject.js.map +1 -0
  101. package/dist/healing/overlay/keybinds.d.ts +33 -0
  102. package/dist/healing/overlay/keybinds.d.ts.map +1 -0
  103. package/dist/healing/overlay/keybinds.js +105 -0
  104. package/dist/healing/overlay/keybinds.js.map +1 -0
  105. package/dist/healing/prompts/heal-v21.d.ts +8 -0
  106. package/dist/healing/prompts/heal-v21.d.ts.map +1 -0
  107. package/dist/healing/prompts/heal-v21.js +204 -0
  108. package/dist/healing/prompts/heal-v21.js.map +1 -0
  109. package/dist/healing/retry-policy.d.ts +37 -0
  110. package/dist/healing/retry-policy.d.ts.map +1 -0
  111. package/dist/healing/retry-policy.js +46 -0
  112. package/dist/healing/retry-policy.js.map +1 -0
  113. package/dist/healing/session-cost.d.ts +44 -0
  114. package/dist/healing/session-cost.d.ts.map +1 -0
  115. package/dist/healing/session-cost.js +95 -0
  116. package/dist/healing/session-cost.js.map +1 -0
  117. package/dist/healing/test-fixture.d.ts +9 -0
  118. package/dist/healing/test-fixture.d.ts.map +1 -0
  119. package/dist/healing/test-fixture.js +37 -0
  120. package/dist/healing/test-fixture.js.map +1 -0
  121. package/dist/healing/types.d.ts +399 -0
  122. package/dist/healing/types.d.ts.map +1 -0
  123. package/dist/healing/types.js +162 -0
  124. package/dist/healing/types.js.map +1 -0
  125. package/dist/healing/validator.d.ts +64 -0
  126. package/dist/healing/validator.d.ts.map +1 -0
  127. package/dist/healing/validator.js +286 -0
  128. package/dist/healing/validator.js.map +1 -0
  129. package/dist/scripts/heal-report.d.ts +23 -0
  130. package/dist/scripts/heal-report.d.ts.map +1 -0
  131. package/dist/scripts/heal-report.js +106 -0
  132. package/dist/scripts/heal-report.js.map +1 -0
  133. package/dist/scripts/init.d.ts +3 -0
  134. package/dist/scripts/init.d.ts.map +1 -0
  135. package/dist/scripts/init.js +132 -0
  136. package/dist/scripts/init.js.map +1 -0
  137. package/package.json +84 -0
@@ -0,0 +1,286 @@
1
+ import { z } from 'zod';
2
+ import { HealingError } from './errors.js';
3
+ /**
4
+ * Capture a stable fingerprint of a DOM element for cache-drift detection.
5
+ * Recomputed on cache hit; entries whose fingerprint has drifted are
6
+ * treated as stale and re-healed instead of trusted blindly.
7
+ *
8
+ * @param handle - Element handle, usually the operator's picked node.
9
+ * @returns Fingerprint (tag + role + name + text + key attrs).
10
+ */
11
+ export async function captureFingerprint(handle) {
12
+ return handle.evaluate((el) => {
13
+ const e = el;
14
+ const attrs = {};
15
+ for (const k of ['id', 'name', 'data-testid', 'data-test', 'data-qa', 'data-cy', 'aria-label']) {
16
+ const v = e.getAttribute(k);
17
+ if (v)
18
+ attrs[k] = v;
19
+ }
20
+ const tag = e.tagName.toLowerCase();
21
+ const role = e.getAttribute('role') ?? '';
22
+ const accessibleName = e.ariaLabel ?? e.getAttribute('aria-label') ?? (e.textContent ?? '').trim().slice(0, 80);
23
+ const text = (e.textContent ?? '').trim().slice(0, 80);
24
+ return {
25
+ tag,
26
+ ...(role ? { role } : {}),
27
+ ...(accessibleName ? { accessibleName } : {}),
28
+ ...(text ? { text } : {}),
29
+ ...(Object.keys(attrs).length ? { attrs } : {}),
30
+ };
31
+ });
32
+ }
33
+ /**
34
+ * Compare two fingerprints. Returns true if they match within a tolerance
35
+ * suitable for "same logical element after a minor DOM change".
36
+ *
37
+ * - tag must match exactly
38
+ * - role, accessibleName, text compared via Levenshtein distance ≤ 30%
39
+ * - any matching stable attr (id, testid, aria-label) short-circuits to match
40
+ *
41
+ * @param a - Cached fingerprint.
42
+ * @param b - Live recomputed fingerprint.
43
+ */
44
+ export function fingerprintMatches(a, b) {
45
+ if (a.tag !== b.tag)
46
+ return false;
47
+ // Stable-attr short-circuit: if any of id/testid/aria-label matches, accept.
48
+ if (a.attrs && b.attrs) {
49
+ for (const k of ['id', 'data-testid', 'data-test', 'data-qa', 'data-cy', 'aria-label']) {
50
+ if (a.attrs[k] && a.attrs[k] === b.attrs[k])
51
+ return true;
52
+ }
53
+ }
54
+ const within = (s1, s2) => {
55
+ if (!s1 && !s2)
56
+ return true;
57
+ if (!s1 || !s2)
58
+ return false;
59
+ if (s1 === s2)
60
+ return true;
61
+ const max = Math.max(s1.length, s2.length);
62
+ if (max === 0)
63
+ return true;
64
+ const dist = levenshtein(s1, s2);
65
+ return dist / max <= 0.3;
66
+ };
67
+ return within(a.role, b.role) && within(a.accessibleName, b.accessibleName) && within(a.text, b.text);
68
+ }
69
+ function levenshtein(a, b) {
70
+ if (a === b)
71
+ return 0;
72
+ if (!a.length)
73
+ return b.length;
74
+ if (!b.length)
75
+ return a.length;
76
+ const prev = new Array(b.length + 1).fill(0).map((_, i) => i);
77
+ for (let i = 1; i <= a.length; i++) {
78
+ let curr = i;
79
+ let topLeft = i - 1;
80
+ for (let j = 1; j <= b.length; j++) {
81
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
82
+ const next = Math.min(curr + 1, prev[j] + 1, topLeft + cost);
83
+ topLeft = prev[j];
84
+ prev[j] = curr;
85
+ curr = next;
86
+ }
87
+ prev[b.length] = curr;
88
+ }
89
+ return prev[b.length];
90
+ }
91
+ // Filter-chain shape: root + optional filters + optional target. Strict
92
+ // rejects any unknown key — enforcing max depth=2 (no nested chain/deeper).
93
+ const FilterChainSpecSchema = z.object({
94
+ root: z.string().min(1),
95
+ filters: z.array(z.record(z.unknown())).optional().default([]),
96
+ target: z.string().optional(),
97
+ }).strict();
98
+ /**
99
+ * If a role suggestion's exact `name="X"` returns no matches, produce a
100
+ * relaxed variant using a case-insensitive regex match. Accessible names
101
+ * commonly carry leading/trailing whitespace introduced by adjacent icons
102
+ * (`<button><i>icon</i> Login</button>` → accessibleName=` Login`), and
103
+ * exact-string matches fail in that case.
104
+ *
105
+ * Returns the relaxed suggestion (new selector string) or null if the input
106
+ * isn't a role+name selector.
107
+ *
108
+ * @param suggestion - Original LLM suggestion.
109
+ * @returns Relaxed suggestion with regex name, or null.
110
+ */
111
+ export function relaxRoleNameMatch(suggestion) {
112
+ if (suggestion.selectorType !== 'role')
113
+ return null;
114
+ const m = suggestion.selector.match(/^([^\[]+)\[name="([^"]+)"\](.*)$/);
115
+ if (!m)
116
+ return null;
117
+ const [, rolePart, name, rest] = m;
118
+ const escaped = name.trim().replace(/[\\/]/g, (s) => `\\${s}`);
119
+ const relaxedSelector = `${rolePart}[name=/${escaped}/i]${rest}`;
120
+ return { ...suggestion, selector: relaxedSelector };
121
+ }
122
+ export function buildLocator(page, suggestion) {
123
+ switch (suggestion.selectorType) {
124
+ case 'role': {
125
+ // LLM sometimes generates aria-label= instead of name=; the role= selector
126
+ // engine only recognizes name=, checked=, disabled= etc. — not aria-label=.
127
+ const normalized = suggestion.selector.replace(/\baria-label=/gi, 'name=');
128
+ return page.locator(`role=${normalized}`);
129
+ }
130
+ case 'text':
131
+ return page.getByText(suggestion.selector, { exact: false });
132
+ case 'label':
133
+ return page.getByLabel(suggestion.selector);
134
+ case 'testid':
135
+ return page.getByTestId(suggestion.selector);
136
+ case 'filter-chain': {
137
+ let raw;
138
+ try {
139
+ raw = JSON.parse(suggestion.selector);
140
+ }
141
+ catch (e) {
142
+ throw new HealingError(`filter-chain JSON parse failed: ${e.message}`, 'invalid-filter-chain');
143
+ }
144
+ const parsed = FilterChainSpecSchema.safeParse(raw);
145
+ if (!parsed.success) {
146
+ throw new HealingError(`filter-chain rejected (depth or shape): ${parsed.error.message}`, 'invalid-filter-chain');
147
+ }
148
+ const spec = parsed.data;
149
+ let loc = page.locator(spec.root);
150
+ for (const f of spec.filters)
151
+ loc = loc.filter(f);
152
+ if (spec.target)
153
+ loc = loc.locator(spec.target);
154
+ return loc;
155
+ }
156
+ case 'js':
157
+ // js tier is validated via direct evaluate path; buildLocator can't build it.
158
+ // Return a never-matching locator so any downstream attempt fails-closed.
159
+ return page.locator('__heal_never_match__');
160
+ case 'css':
161
+ case 'xpath':
162
+ default:
163
+ return page.locator(suggestion.selector);
164
+ }
165
+ }
166
+ export async function validateLocator(page, suggestion, timeoutMs = 5000) {
167
+ try {
168
+ const locator = buildLocator(page, suggestion);
169
+ // Wait for at least one match to be visible (handles JS-rendered content),
170
+ // then require exactly one match so we never cache an ambiguous locator.
171
+ await locator.first().waitFor({ state: 'visible', timeout: timeoutMs });
172
+ const count = await locator.count();
173
+ return count === 1;
174
+ }
175
+ catch {
176
+ return false;
177
+ }
178
+ }
179
+ export async function validateProposedSelector(page, selector, picked) {
180
+ let count;
181
+ try {
182
+ count = await page.locator(selector).count();
183
+ }
184
+ catch (err) {
185
+ return { ok: false, count: 0, reason: `invalid selector: ${err.message}` };
186
+ }
187
+ if (count !== 1) {
188
+ return { ok: false, count, reason: `matched ${count}, expected 1` };
189
+ }
190
+ const matched = await page.locator(selector).elementHandle();
191
+ const same = matched && (await matched.evaluate((a, b) => a === b, picked));
192
+ if (!same)
193
+ return { ok: false, count, reason: 'matched a different node than picked' };
194
+ return { ok: true, count };
195
+ }
196
+ export async function validateProposedLocator(locator, picked) {
197
+ let count = 0;
198
+ try {
199
+ count = await locator.count();
200
+ }
201
+ catch (err) {
202
+ const msg = String(err?.message ?? err);
203
+ if (msg.includes('strict mode violation'))
204
+ return { ok: false, count: -1, reason: 'strict-mode-violation' };
205
+ return { ok: false, count: 0, reason: `invalid locator: ${msg}` };
206
+ }
207
+ if (count !== 1)
208
+ return { ok: false, count, reason: `matched ${count}, expected 1` };
209
+ try {
210
+ const visible = await locator.isVisible();
211
+ if (!visible)
212
+ return { ok: false, count, reason: 'not visible' };
213
+ }
214
+ catch (err) {
215
+ return { ok: false, count, reason: `visibility check failed: ${err?.message ?? err}` };
216
+ }
217
+ // P0-4 identity check: when the operator picked a specific DOM node in the
218
+ // overlay, the LLM's proposed selector must resolve to that same node. A
219
+ // uniquely-resolving sibling that passes count===1 + visible would
220
+ // otherwise be cached and poison every subsequent run.
221
+ if (picked) {
222
+ try {
223
+ const resolvedHandle = await locator.elementHandle();
224
+ if (!resolvedHandle)
225
+ return { ok: false, count, reason: 'identity-drift: resolved no handle' };
226
+ const same = await resolvedHandle.evaluate((a, b) => a === b, picked);
227
+ if (!same)
228
+ return { ok: false, count, reason: 'identity-drift: resolved a different node' };
229
+ }
230
+ catch (err) {
231
+ return { ok: false, count, reason: `identity check failed: ${err?.message ?? err}` };
232
+ }
233
+ }
234
+ return { ok: true, count };
235
+ }
236
+ /**
237
+ * Verify a candidate locator is actionable for the requested action BEFORE
238
+ * writing it to cache. Without this check the healer can cache a selector
239
+ * that resolves count===1 + visible but isn't actually clickable (overlay,
240
+ * pointer-events:none, disabled). Cache then poisons every subsequent run.
241
+ *
242
+ * Uses Playwright's trial mode for mutating actions: the action is fully
243
+ * simulated (visibility, stability, enabledness, no overlap) but not
244
+ * performed. For read-only actions a lighter check suffices.
245
+ *
246
+ * @param locator - Built locator under validation.
247
+ * @param action - Method name the test will invoke on success (`click`, `fill`...).
248
+ * @param timeoutMs - Budget per probe.
249
+ * @returns `{ ok }` plus a `reason` describing the first failing check.
250
+ */
251
+ export async function verifyActionable(locator, action, timeoutMs = 1500) {
252
+ const TRIAL_ACTIONS = new Set(['click', 'dblclick', 'tap', 'hover', 'check', 'uncheck']);
253
+ const EDIT_ACTIONS = new Set(['fill', 'type', 'press', 'pressSequentially', 'clear', 'setInputFiles', 'selectText', 'selectOption']);
254
+ try {
255
+ if (TRIAL_ACTIONS.has(action)) {
256
+ // Playwright's `trial: true` runs the full actionability waiter
257
+ // (visible, stable, enabled, receives events) without performing.
258
+ await locator[action]({ trial: true, timeout: timeoutMs });
259
+ return { ok: true, count: 1 };
260
+ }
261
+ if (EDIT_ACTIONS.has(action)) {
262
+ const visible = await locator.isVisible();
263
+ if (!visible)
264
+ return { ok: false, count: 1, reason: 'edit target not visible' };
265
+ const enabled = await locator.isEnabled();
266
+ if (!enabled)
267
+ return { ok: false, count: 1, reason: 'edit target not enabled' };
268
+ if (action === 'fill' || action === 'type' || action === 'clear' || action === 'pressSequentially') {
269
+ const editable = await locator.isEditable().catch(() => true);
270
+ if (!editable)
271
+ return { ok: false, count: 1, reason: 'edit target not editable' };
272
+ }
273
+ return { ok: true, count: 1 };
274
+ }
275
+ // assertions, queries, focus, scrollIntoViewIfNeeded — visibility is enough
276
+ const visible = await locator.isVisible();
277
+ if (!visible)
278
+ return { ok: false, count: 1, reason: 'not visible' };
279
+ return { ok: true, count: 1 };
280
+ }
281
+ catch (err) {
282
+ const msg = String(err?.message ?? err);
283
+ return { ok: false, count: 1, reason: `actionability failed: ${msg.split('\n')[0].slice(0, 200)}` };
284
+ }
285
+ }
286
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../healing/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/heal-report.ts
4
+ *
5
+ * Reads `.healed-locators/cost.daily.json` and prints a markdown table:
6
+ *
7
+ * | date | calls | cost (USD) | cache hit rate | cumulative cost (USD) |
8
+ *
9
+ * Sorted newest day first. Cumulative is computed by accumulating cost from
10
+ * the oldest day forward, then displayed against each row. Cache hit rate is
11
+ * `cachedCalls / calls`; displayed as `n/a` for rows missing `cachedCalls`
12
+ * (pre-cache-tracking historical data).
13
+ *
14
+ * Usage:
15
+ * npx tsx scripts/heal-report.ts
16
+ * npx tsx scripts/heal-report.ts --file path/to/cost.daily.json
17
+ *
18
+ * Exit codes:
19
+ * 0 ok (table or "no data" notice printed)
20
+ * 1 file read/parse error
21
+ */
22
+ export {};
23
+ //# sourceMappingURL=heal-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heal-report.d.ts","sourceRoot":"","sources":["../../scripts/heal-report.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;GAmBG"}
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/heal-report.ts
4
+ *
5
+ * Reads `.healed-locators/cost.daily.json` and prints a markdown table:
6
+ *
7
+ * | date | calls | cost (USD) | cache hit rate | cumulative cost (USD) |
8
+ *
9
+ * Sorted newest day first. Cumulative is computed by accumulating cost from
10
+ * the oldest day forward, then displayed against each row. Cache hit rate is
11
+ * `cachedCalls / calls`; displayed as `n/a` for rows missing `cachedCalls`
12
+ * (pre-cache-tracking historical data).
13
+ *
14
+ * Usage:
15
+ * npx tsx scripts/heal-report.ts
16
+ * npx tsx scripts/heal-report.ts --file path/to/cost.daily.json
17
+ *
18
+ * Exit codes:
19
+ * 0 ok (table or "no data" notice printed)
20
+ * 1 file read/parse error
21
+ */
22
+ import { readFileSync, existsSync } from 'node:fs';
23
+ import { join, resolve } from 'node:path';
24
+ function parseArgs(argv) {
25
+ let file = join(process.cwd(), '.healed-locators', 'cost.daily.json');
26
+ for (let i = 0; i < argv.length; i++) {
27
+ if ((argv[i] === '--file' || argv[i] === '-f') && argv[i + 1]) {
28
+ file = resolve(argv[i + 1]);
29
+ i++;
30
+ }
31
+ }
32
+ return { file };
33
+ }
34
+ function fmtCost(n) {
35
+ return `$${n.toFixed(4)}`;
36
+ }
37
+ function fmtRate(hits, calls) {
38
+ if (hits === undefined || calls === 0)
39
+ return 'n/a';
40
+ return `${((hits / calls) * 100).toFixed(1)}%`;
41
+ }
42
+ function buildTable(data) {
43
+ const entries = Object.entries(data.byDay);
44
+ if (entries.length === 0) {
45
+ return '_(no recorded LLM calls — `.healed-locators/cost.daily.json` is empty)_';
46
+ }
47
+ // Sort oldest → newest to compute cumulative, then reverse for display.
48
+ entries.sort(([a], [b]) => a.localeCompare(b));
49
+ let running = 0;
50
+ const rows = entries.map(([date, row]) => {
51
+ running += row.cost;
52
+ return {
53
+ date,
54
+ calls: row.calls,
55
+ cost: row.cost,
56
+ cumulative: running,
57
+ hitRate: fmtRate(row.cachedCalls, row.calls),
58
+ };
59
+ });
60
+ rows.reverse();
61
+ const lines = [];
62
+ lines.push('| date | calls | cost | cache hit rate | cumulative |');
63
+ lines.push('|------|------:|-----:|---------------:|-----------:|');
64
+ for (const r of rows) {
65
+ lines.push(`| ${r.date} | ${r.calls} | ${fmtCost(r.cost)} | ${r.hitRate} | ${fmtCost(r.cumulative)} |`);
66
+ }
67
+ const all = data.allTime;
68
+ lines.push(`| **all-time** | **${all.calls}** | **${fmtCost(all.cost)}** | **${fmtRate(all.cachedCalls, all.calls)}** | **${fmtCost(all.cost)}** |`);
69
+ return lines.join('\n');
70
+ }
71
+ function main() {
72
+ const { file } = parseArgs(process.argv.slice(2));
73
+ if (!existsSync(file)) {
74
+ process.stdout.write(`# Heal cost report\n\n_(no data — ${file} does not exist)_\n`);
75
+ return 0;
76
+ }
77
+ let raw;
78
+ try {
79
+ raw = readFileSync(file, 'utf-8');
80
+ }
81
+ catch (err) {
82
+ process.stderr.write(`error: cannot read ${file}: ${err.message}\n`);
83
+ return 1;
84
+ }
85
+ let data;
86
+ try {
87
+ data = JSON.parse(raw);
88
+ }
89
+ catch (err) {
90
+ process.stderr.write(`error: cannot parse ${file} as JSON: ${err.message}\n`);
91
+ return 1;
92
+ }
93
+ // Defensive: tolerate partial shapes.
94
+ if (!data || typeof data !== 'object' || !data.byDay) {
95
+ data = { byDay: {}, allTime: { cost: 0, calls: 0 } };
96
+ }
97
+ if (!data.allTime)
98
+ data.allTime = { cost: 0, calls: 0 };
99
+ process.stdout.write('# Heal cost report\n\n');
100
+ process.stdout.write(`Source: \`${file}\`\n\n`);
101
+ process.stdout.write(buildTable(data));
102
+ process.stdout.write('\n');
103
+ return 0;
104
+ }
105
+ process.exit(main());
106
+ //# sourceMappingURL=heal-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heal-report.js","sourceRoot":"","sources":["../../scripts/heal-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../scripts/init.ts"],"names":[],"mappings":""}
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/init.ts
4
+ *
5
+ * `npx playwright-locator-healer init` — one-command setup for a consumer
6
+ * project. Idempotent: re-running is safe.
7
+ *
8
+ * Steps performed (each is a no-op if already done):
9
+ * 1. Verify peer dep — @playwright/test ≥ 1.59
10
+ * 2. Verify OPENAI_API_KEY presence (env or .env)
11
+ * 3. Write .env.example if missing
12
+ * 4. Create .healed-locators/.gitkeep (cache dir tracked, contents kept)
13
+ * 5. Append safe entries to .gitignore (cache screenshots + logs only)
14
+ * 6. Print next-steps banner
15
+ */
16
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync } from 'node:fs';
17
+ import { join, resolve } from 'node:path';
18
+ const ENV_EXAMPLE_TEMPLATE = `# OpenAI key — required when a heal fires (cache hits do not need it)
19
+ OPENAI_API_KEY=
20
+
21
+ # Optional — see healing/models.ts for full registry
22
+ # HEAL_MODEL=gpt-4o-mini
23
+
24
+ # Optional — debug|info|warn|error (default warn)
25
+ # HEAL_LOG_LEVEL=warn
26
+
27
+ # Optional — defaults to .healed-locators relative to cwd
28
+ # HEAL_CACHE_DIR=.healed-locators
29
+ `;
30
+ const GITIGNORE_ENTRIES = [
31
+ '# playwright-heal — screenshot evidence + logs (cache JSON stays tracked)',
32
+ '.healed-locators/screenshots/',
33
+ '.healed-locators/*.log',
34
+ ];
35
+ function log(prefix, msg) {
36
+ process.stdout.write(`${prefix} ${msg}\n`);
37
+ }
38
+ function checkPlaywright(cwd) {
39
+ const pkgPath = join(cwd, 'package.json');
40
+ if (!existsSync(pkgPath)) {
41
+ return { step: 'peer-dep', ok: false, message: 'no package.json in cwd — run from your project root' };
42
+ }
43
+ let pkg;
44
+ try {
45
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
46
+ }
47
+ catch (e) {
48
+ return { step: 'peer-dep', ok: false, message: `package.json invalid: ${e.message}` };
49
+ }
50
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
51
+ if (!deps['@playwright/test']) {
52
+ return { step: 'peer-dep', ok: false, message: '@playwright/test not installed — run `npm install --save-dev @playwright/test`' };
53
+ }
54
+ return { step: 'peer-dep', ok: true, message: `@playwright/test ${deps['@playwright/test']} detected` };
55
+ }
56
+ function checkOpenAIKey(cwd) {
57
+ if (process.env.OPENAI_API_KEY) {
58
+ return { step: 'api-key', ok: true, message: 'OPENAI_API_KEY set in shell env' };
59
+ }
60
+ const envPath = join(cwd, '.env');
61
+ if (existsSync(envPath)) {
62
+ const txt = readFileSync(envPath, 'utf-8');
63
+ const match = txt.match(/^OPENAI_API_KEY=(.+)$/m);
64
+ if (match && match[1].trim().length > 0) {
65
+ return { step: 'api-key', ok: true, message: 'OPENAI_API_KEY found in .env' };
66
+ }
67
+ }
68
+ return { step: 'api-key', ok: false, message: 'OPENAI_API_KEY not set — see .env.example for placeholder, then add to .env or shell' };
69
+ }
70
+ function ensureEnvExample(cwd) {
71
+ const path = join(cwd, '.env.example');
72
+ if (existsSync(path))
73
+ return { step: 'env-example', ok: true, message: '.env.example already present (unchanged)' };
74
+ writeFileSync(path, ENV_EXAMPLE_TEMPLATE);
75
+ return { step: 'env-example', ok: true, message: 'wrote .env.example' };
76
+ }
77
+ function ensureCacheDir(cwd) {
78
+ const dir = join(cwd, '.healed-locators');
79
+ const keep = join(dir, '.gitkeep');
80
+ if (!existsSync(dir))
81
+ mkdirSync(dir, { recursive: true });
82
+ if (!existsSync(keep))
83
+ writeFileSync(keep, '');
84
+ return { step: 'cache-dir', ok: true, message: `${dir} ready (.gitkeep present, commit the directory to share heals)` };
85
+ }
86
+ function ensureGitignore(cwd) {
87
+ const path = join(cwd, '.gitignore');
88
+ const current = existsSync(path) ? readFileSync(path, 'utf-8') : '';
89
+ const missing = GITIGNORE_ENTRIES.filter((e) => !e.startsWith('#') && !current.includes(e));
90
+ if (missing.length === 0) {
91
+ return { step: 'gitignore', ok: true, message: '.gitignore already covers heal artifacts (unchanged)' };
92
+ }
93
+ const sep = current.length === 0 || current.endsWith('\n') ? '' : '\n';
94
+ const block = `${sep}\n${GITIGNORE_ENTRIES.join('\n')}\n`;
95
+ appendFileSync(path, block);
96
+ return { step: 'gitignore', ok: true, message: `added ${missing.length} entries to .gitignore` };
97
+ }
98
+ function printNextSteps(allOk) {
99
+ process.stdout.write('\n');
100
+ log('[heal]', '────────── next steps ──────────');
101
+ log('[heal]', '1. Change your spec import:');
102
+ log('[heal]', ` import { test, expect } from 'playwright-locator-healer/auto';`);
103
+ log('[heal]', '2. Run your tests:');
104
+ log('[heal]', ' npx playwright test --headed');
105
+ log('[heal]', '3. When a locator breaks, an overlay opens — pick the element, accept, done.');
106
+ log('[heal]', '4. Cost report any time:');
107
+ log('[heal]', ' npx heal-report');
108
+ if (!allOk) {
109
+ log('[heal]', '');
110
+ log('[heal]', 'Some checks above need your attention before tests will heal cleanly.');
111
+ }
112
+ process.stdout.write('\n');
113
+ }
114
+ function main() {
115
+ const cwd = resolve(process.cwd());
116
+ log('[heal]', `init in ${cwd}`);
117
+ const steps = [
118
+ checkPlaywright(cwd),
119
+ ensureEnvExample(cwd),
120
+ checkOpenAIKey(cwd),
121
+ ensureCacheDir(cwd),
122
+ ensureGitignore(cwd),
123
+ ];
124
+ for (const s of steps) {
125
+ log(s.ok ? '[heal] ✓' : '[heal] ✗', `${s.step.padEnd(14)} ${s.message}`);
126
+ }
127
+ const allOk = steps.every((s) => s.ok);
128
+ printNextSteps(allOk);
129
+ process.exit(allOk ? 0 : 1);
130
+ }
131
+ main();
132
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../scripts/init.ts"],"names":[],"mappings":"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"}