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.
- package/CHANGELOG.md +71 -0
- package/LICENSE +21 -0
- package/README.md +338 -0
- package/USAGE.md +217 -0
- package/dist/healing/audit-log.d.ts +9 -0
- package/dist/healing/audit-log.d.ts.map +1 -0
- package/dist/healing/audit-log.js +22 -0
- package/dist/healing/audit-log.js.map +1 -0
- package/dist/healing/auto-fixture.d.ts +14 -0
- package/dist/healing/auto-fixture.d.ts.map +1 -0
- package/dist/healing/auto-fixture.js +430 -0
- package/dist/healing/auto-fixture.js.map +1 -0
- package/dist/healing/cache.d.ts +11 -0
- package/dist/healing/cache.d.ts.map +1 -0
- package/dist/healing/cache.js +128 -0
- package/dist/healing/cache.js.map +1 -0
- package/dist/healing/circuit-breaker.d.ts +11 -0
- package/dist/healing/circuit-breaker.d.ts.map +1 -0
- package/dist/healing/circuit-breaker.js +34 -0
- package/dist/healing/circuit-breaker.js.map +1 -0
- package/dist/healing/cost-tracker.d.ts +30 -0
- package/dist/healing/cost-tracker.d.ts.map +1 -0
- package/dist/healing/cost-tracker.js +101 -0
- package/dist/healing/cost-tracker.js.map +1 -0
- package/dist/healing/dom-extractor.d.ts +9 -0
- package/dist/healing/dom-extractor.d.ts.map +1 -0
- package/dist/healing/dom-extractor.js +268 -0
- package/dist/healing/dom-extractor.js.map +1 -0
- package/dist/healing/errors.d.ts +33 -0
- package/dist/healing/errors.d.ts.map +1 -0
- package/dist/healing/errors.js +40 -0
- package/dist/healing/errors.js.map +1 -0
- package/dist/healing/heal/caller-context.d.ts +8 -0
- package/dist/healing/heal/caller-context.d.ts.map +1 -0
- package/dist/healing/heal/caller-context.js +46 -0
- package/dist/healing/heal/caller-context.js.map +1 -0
- package/dist/healing/heal/orchestrator-helpers.d.ts +84 -0
- package/dist/healing/heal/orchestrator-helpers.d.ts.map +1 -0
- package/dist/healing/heal/orchestrator-helpers.js +117 -0
- package/dist/healing/heal/orchestrator-helpers.js.map +1 -0
- package/dist/healing/heal/orchestrator.d.ts +47 -0
- package/dist/healing/heal/orchestrator.d.ts.map +1 -0
- package/dist/healing/heal/orchestrator.js +644 -0
- package/dist/healing/heal/orchestrator.js.map +1 -0
- package/dist/healing/heal/playwright-code.d.ts +26 -0
- package/dist/healing/heal/playwright-code.d.ts.map +1 -0
- package/dist/healing/heal/playwright-code.js +134 -0
- package/dist/healing/heal/playwright-code.js.map +1 -0
- package/dist/healing/heal/preflight.d.ts +8 -0
- package/dist/healing/heal/preflight.d.ts.map +1 -0
- package/dist/healing/heal/preflight.js +77 -0
- package/dist/healing/heal/preflight.js.map +1 -0
- package/dist/healing/heal/prompt.d.ts +9 -0
- package/dist/healing/heal/prompt.d.ts.map +1 -0
- package/dist/healing/heal/prompt.js +22 -0
- package/dist/healing/heal/prompt.js.map +1 -0
- package/dist/healing/heal/report.d.ts +11 -0
- package/dist/healing/heal/report.d.ts.map +1 -0
- package/dist/healing/heal/report.js +28 -0
- package/dist/healing/heal/report.js.map +1 -0
- package/dist/healing/heal/source-trace.d.ts +17 -0
- package/dist/healing/heal/source-trace.d.ts.map +1 -0
- package/dist/healing/heal/source-trace.js +59 -0
- package/dist/healing/heal/source-trace.js.map +1 -0
- package/dist/healing/index.d.ts +6 -0
- package/dist/healing/index.d.ts.map +1 -0
- package/dist/healing/index.js +3 -0
- package/dist/healing/index.js.map +1 -0
- package/dist/healing/llm-client.d.ts +58 -0
- package/dist/healing/llm-client.d.ts.map +1 -0
- package/dist/healing/llm-client.js +258 -0
- package/dist/healing/llm-client.js.map +1 -0
- package/dist/healing/logger.d.ts +18 -0
- package/dist/healing/logger.d.ts.map +1 -0
- package/dist/healing/logger.js +38 -0
- package/dist/healing/logger.js.map +1 -0
- package/dist/healing/models.d.ts +26 -0
- package/dist/healing/models.d.ts.map +1 -0
- package/dist/healing/models.js +109 -0
- package/dist/healing/models.js.map +1 -0
- package/dist/healing/overlay/bridge.d.ts +46 -0
- package/dist/healing/overlay/bridge.d.ts.map +1 -0
- package/dist/healing/overlay/bridge.js +2 -0
- package/dist/healing/overlay/bridge.js.map +1 -0
- package/dist/healing/overlay/dock.css.d.ts +2 -0
- package/dist/healing/overlay/dock.css.d.ts.map +1 -0
- package/dist/healing/overlay/dock.css.js +448 -0
- package/dist/healing/overlay/dock.css.js.map +1 -0
- package/dist/healing/overlay/dock.html.d.ts +46 -0
- package/dist/healing/overlay/dock.html.d.ts.map +1 -0
- package/dist/healing/overlay/dock.html.js +248 -0
- package/dist/healing/overlay/dock.html.js.map +1 -0
- package/dist/healing/overlay/drag.d.ts +17 -0
- package/dist/healing/overlay/drag.d.ts.map +1 -0
- package/dist/healing/overlay/drag.js +68 -0
- package/dist/healing/overlay/drag.js.map +1 -0
- package/dist/healing/overlay/inject.d.ts +41 -0
- package/dist/healing/overlay/inject.d.ts.map +1 -0
- package/dist/healing/overlay/inject.js +277 -0
- package/dist/healing/overlay/inject.js.map +1 -0
- package/dist/healing/overlay/keybinds.d.ts +33 -0
- package/dist/healing/overlay/keybinds.d.ts.map +1 -0
- package/dist/healing/overlay/keybinds.js +105 -0
- package/dist/healing/overlay/keybinds.js.map +1 -0
- package/dist/healing/prompts/heal-v21.d.ts +8 -0
- package/dist/healing/prompts/heal-v21.d.ts.map +1 -0
- package/dist/healing/prompts/heal-v21.js +204 -0
- package/dist/healing/prompts/heal-v21.js.map +1 -0
- package/dist/healing/retry-policy.d.ts +37 -0
- package/dist/healing/retry-policy.d.ts.map +1 -0
- package/dist/healing/retry-policy.js +46 -0
- package/dist/healing/retry-policy.js.map +1 -0
- package/dist/healing/session-cost.d.ts +44 -0
- package/dist/healing/session-cost.d.ts.map +1 -0
- package/dist/healing/session-cost.js +95 -0
- package/dist/healing/session-cost.js.map +1 -0
- package/dist/healing/test-fixture.d.ts +9 -0
- package/dist/healing/test-fixture.d.ts.map +1 -0
- package/dist/healing/test-fixture.js +37 -0
- package/dist/healing/test-fixture.js.map +1 -0
- package/dist/healing/types.d.ts +399 -0
- package/dist/healing/types.d.ts.map +1 -0
- package/dist/healing/types.js +162 -0
- package/dist/healing/types.js.map +1 -0
- package/dist/healing/validator.d.ts +64 -0
- package/dist/healing/validator.d.ts.map +1 -0
- package/dist/healing/validator.js +286 -0
- package/dist/healing/validator.js.map +1 -0
- package/dist/scripts/heal-report.d.ts +23 -0
- package/dist/scripts/heal-report.d.ts.map +1 -0
- package/dist/scripts/heal-report.js +106 -0
- package/dist/scripts/heal-report.js.map +1 -0
- package/dist/scripts/init.d.ts +3 -0
- package/dist/scripts/init.d.ts.map +1 -0
- package/dist/scripts/init.js +132 -0
- package/dist/scripts/init.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* healing/llm-client.ts
|
|
3
|
+
*
|
|
4
|
+
* Thin OpenAI client for the heal v2.1 interactive flow. Single public
|
|
5
|
+
* entry point: `requestHealedLocator(input)`. Concerns split out:
|
|
6
|
+
* - prompts → ./prompts/heal-v21.ts
|
|
7
|
+
* - retries → ./retry-policy.ts
|
|
8
|
+
* - session cost → ./session-cost.ts
|
|
9
|
+
* - circuit-break → ./circuit-breaker.ts
|
|
10
|
+
* - cost tracking → ./cost-tracker.ts (lazy import — optional dep)
|
|
11
|
+
*
|
|
12
|
+
* The module is intentionally minimal: build prompt, call OpenAI with
|
|
13
|
+
* timeout + retry + circuit-breaker, accumulate cost, return suggestions.
|
|
14
|
+
*/
|
|
15
|
+
import OpenAI from 'openai';
|
|
16
|
+
import { zodResponseFormat } from 'openai/helpers/zod';
|
|
17
|
+
import { appendFile, mkdir } from 'node:fs/promises';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { HealingError } from './errors.js';
|
|
20
|
+
import { warn as logWarn } from './logger.js';
|
|
21
|
+
import { HEAL_V21_SYSTEM_PROMPT } from './prompts/heal-v21.js';
|
|
22
|
+
import { backoffWithJitter, sleep } from './retry-policy.js';
|
|
23
|
+
import { ACTIVE_MODEL_ID, PRICE_INPUT_FRESH_PER_M, PRICE_INPUT_CACHED_PER_M, PRICE_OUTPUT_PER_M, accumulateSessionCost, } from './session-cost.js';
|
|
24
|
+
import { isOpen as isCircuitOpen, recordSuccess as recordCircuitSuccess, recordFailure as recordCircuitFailure, } from './circuit-breaker.js';
|
|
25
|
+
import { LocatorSuggestionsResponseSchema, } from './types.js';
|
|
26
|
+
// Back-compat re-exports — keep stable import paths for unit tests + consumers.
|
|
27
|
+
export { backoffWithJitter } from './retry-policy.js';
|
|
28
|
+
export { ACTIVE_MODEL_ID, accumulateSessionCost, getSessionCost, resetSessionCost, } from './session-cost.js';
|
|
29
|
+
// ── OpenAI client (lazy) ─────────────────────────────────────────────────────
|
|
30
|
+
// API key check deferred to first call so importing healing/* doesn't crash
|
|
31
|
+
// in tests that never reach the LLM (stage 0/1 cache-only paths).
|
|
32
|
+
let _client = null;
|
|
33
|
+
function getClient() {
|
|
34
|
+
if (_client)
|
|
35
|
+
return _client;
|
|
36
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
37
|
+
throw new HealingError('OpenAI API key is not set. Set OPENAI_API_KEY in your environment or .env file.', 'config');
|
|
38
|
+
}
|
|
39
|
+
_client = new OpenAI({ maxRetries: 0 });
|
|
40
|
+
return _client;
|
|
41
|
+
}
|
|
42
|
+
// ── Concurrency semaphore (max 5 simultaneous OpenAI calls) ──────────────────
|
|
43
|
+
const MAX_CONCURRENT = 5;
|
|
44
|
+
let activeRequests = 0;
|
|
45
|
+
const queue = [];
|
|
46
|
+
async function withConcurrencyLimit(fn) {
|
|
47
|
+
if (activeRequests >= MAX_CONCURRENT)
|
|
48
|
+
await new Promise((r) => queue.push(r));
|
|
49
|
+
activeRequests++;
|
|
50
|
+
try {
|
|
51
|
+
return await fn();
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
activeRequests--;
|
|
55
|
+
queue.shift()?.();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ── Per-call timeout (ms) ────────────────────────────────────────────────────
|
|
59
|
+
const LLM_TIMEOUT_MS = Number(process.env.HEALING_TIMEOUT_LLM ?? 15000);
|
|
60
|
+
// ── Per-call raw-usage audit log (ground truth from OpenAI) ──────────────────
|
|
61
|
+
// One NDJSON line per LLM call into .healed-locators/llm-calls.ndjson.
|
|
62
|
+
// The exact `usage` object is preserved verbatim so cache hits can be
|
|
63
|
+
// independently verified against OpenAI's own server response.
|
|
64
|
+
async function logLlmCall(entry) {
|
|
65
|
+
const dir = join(process.cwd(), '.healed-locators');
|
|
66
|
+
const file = join(dir, 'llm-calls.ndjson');
|
|
67
|
+
await mkdir(dir, { recursive: true }).catch((err) => {
|
|
68
|
+
logWarn('best-effort audit mkdir failed:', err.message);
|
|
69
|
+
});
|
|
70
|
+
const line = JSON.stringify({ ts: new Date().toISOString(), ...entry }) + '\n';
|
|
71
|
+
await appendFile(file, line, 'utf-8').catch((err) => {
|
|
72
|
+
logWarn(`best-effort audit append failed file=${file} err=${err.message}`);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function fmtPicked(p) {
|
|
76
|
+
const dataAttrLines = Object.entries(p.dataAttrs).map(([k, v]) => ` ${k}: "${v}"`).join('\n');
|
|
77
|
+
return [
|
|
78
|
+
`tag: ${p.tag}`,
|
|
79
|
+
`id: ${p.id || '(none)'}`,
|
|
80
|
+
`classes: ${p.classes.length ? p.classes.join(' ') : '(none)'}`,
|
|
81
|
+
`text: "${p.text}"`,
|
|
82
|
+
`aria-label: "${p.ariaLabel || '(none)'}"`,
|
|
83
|
+
`aria role: ${p.ariaRole || '(implicit)'}`,
|
|
84
|
+
`accessible name: "${p.accessibleName || '(none)'}"`,
|
|
85
|
+
`data-testid: "${p.dataTestId || '(none)'}"`,
|
|
86
|
+
`data-* attrs: ${Object.keys(p.dataAttrs).length ? '\n' + dataAttrLines : '(none)'}`,
|
|
87
|
+
`name: "${p.name || '(none)'}"`,
|
|
88
|
+
`type: "${p.type || '(none)'}"`,
|
|
89
|
+
`placeholder: "${p.placeholder || '(none)'}"`,
|
|
90
|
+
`href: "${p.href || '(none)'}"`,
|
|
91
|
+
`associated <label>: "${p.associatedLabel || '(none)'}"`,
|
|
92
|
+
`parent role/landmark: ${p.parentRole || '(none)'}`,
|
|
93
|
+
`siblings_same_tag: ${p.siblingsSameTag}`,
|
|
94
|
+
'',
|
|
95
|
+
'outerHTML:',
|
|
96
|
+
p.outerHTML,
|
|
97
|
+
].join('\n');
|
|
98
|
+
}
|
|
99
|
+
function fmtCand(c, idx) {
|
|
100
|
+
const parts = [`[${idx}] <${c.tag}>`];
|
|
101
|
+
if (c.id)
|
|
102
|
+
parts.push(`id="${c.id}"`);
|
|
103
|
+
if (c.dataTestId)
|
|
104
|
+
parts.push(`testid="${c.dataTestId}"`);
|
|
105
|
+
if (c.ariaLabel)
|
|
106
|
+
parts.push(`aria-label="${c.ariaLabel}"`);
|
|
107
|
+
if (c.role)
|
|
108
|
+
parts.push(`role="${c.role}"`);
|
|
109
|
+
if (c.accessibleName && c.accessibleName !== c.text)
|
|
110
|
+
parts.push(`acc-name="${c.accessibleName}"`);
|
|
111
|
+
if (c.text)
|
|
112
|
+
parts.push(`text="${c.text}"`);
|
|
113
|
+
if (c.name)
|
|
114
|
+
parts.push(`name="${c.name}"`);
|
|
115
|
+
if (c.placeholder)
|
|
116
|
+
parts.push(`placeholder="${c.placeholder}"`);
|
|
117
|
+
if (c.href)
|
|
118
|
+
parts.push(`href="${c.href}"`);
|
|
119
|
+
if (c.associatedLabel)
|
|
120
|
+
parts.push(`label="${c.associatedLabel}"`);
|
|
121
|
+
if (c.parentRole)
|
|
122
|
+
parts.push(`parentRole="${c.parentRole}"`);
|
|
123
|
+
if (c.siblingsSameTag != null)
|
|
124
|
+
parts.push(`siblings=${c.siblingsSameTag}`);
|
|
125
|
+
if (c.neighborText)
|
|
126
|
+
parts.push(`neighbors="${c.neighborText.slice(0, 100)}"`);
|
|
127
|
+
return parts.join(' ');
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Call the LLM for a healed locator. Concurrency-limited (≤5 in flight),
|
|
131
|
+
* 3-attempt retry with jittered exponential backoff, 15 s per-call timeout,
|
|
132
|
+
* circuit-breaker gated. Aborts immediately on `signal.abort()`.
|
|
133
|
+
*
|
|
134
|
+
* @param input - Picked element, ancestor HTML, intent, action, candidates,
|
|
135
|
+
* previous attempts, round (1-4), and optional `AbortSignal`.
|
|
136
|
+
* @returns Array of ranked `LocatorSuggestion` objects (length 3 per the
|
|
137
|
+
* prompt contract).
|
|
138
|
+
* @throws HealingError with `code` of `circuit-open`, `aborted`, `refusal`,
|
|
139
|
+
* `auth`, or `network` per failure mode.
|
|
140
|
+
*/
|
|
141
|
+
export async function requestHealedLocator(input) {
|
|
142
|
+
const signal = input.signal;
|
|
143
|
+
const pickedBlock = fmtPicked(input.picked);
|
|
144
|
+
const candBlock = input.candidates.map((c, i) => fmtCand(c, i + 1)).join('\n');
|
|
145
|
+
const prevBlock = input.previousAttempts.length
|
|
146
|
+
? input.previousAttempts.map((a, i) => ` [${i + 1}] tier=${a.tier} type=${a.selectorType} selector=${JSON.stringify(a.selector)} failed: ${a.reason}${a.matchedCount != null ? ' (n=' + a.matchedCount + ')' : ''}`).join('\n')
|
|
147
|
+
: ' (none)';
|
|
148
|
+
const userMessage = `Round: ${input.round}
|
|
149
|
+
Original failed selector: ${input.originalSelector}
|
|
150
|
+
Action: ${input.action}
|
|
151
|
+
Intent: ${input.intent}
|
|
152
|
+
Page URL: ${input.pageUrl}
|
|
153
|
+
|
|
154
|
+
Picked element (structured fields):
|
|
155
|
+
${pickedBlock}
|
|
156
|
+
|
|
157
|
+
Ancestor context (up to 3 levels):
|
|
158
|
+
${input.ancestorsHtml}
|
|
159
|
+
|
|
160
|
+
Candidates (${input.candidates.length}):
|
|
161
|
+
${candBlock}
|
|
162
|
+
|
|
163
|
+
Previous failed attempts:
|
|
164
|
+
${prevBlock}
|
|
165
|
+
|
|
166
|
+
Generate exactly 3 ranked locator suggestions following the strict tier priority and round semantics above.`;
|
|
167
|
+
if (isCircuitOpen()) {
|
|
168
|
+
throw new HealingError('LLM circuit breaker is OPEN — too many recent failures. Reset via resetCircuit() or restart the process.', 'circuit-open');
|
|
169
|
+
}
|
|
170
|
+
return withConcurrencyLimit(async () => {
|
|
171
|
+
let lastErr;
|
|
172
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
173
|
+
try {
|
|
174
|
+
const completionPromise = getClient().beta.chat.completions.parse({
|
|
175
|
+
model: ACTIVE_MODEL_ID,
|
|
176
|
+
messages: [
|
|
177
|
+
{ role: 'system', content: HEAL_V21_SYSTEM_PROMPT },
|
|
178
|
+
{ role: 'user', content: userMessage },
|
|
179
|
+
],
|
|
180
|
+
response_format: zodResponseFormat(LocatorSuggestionsResponseSchema, 'suggestions'),
|
|
181
|
+
temperature: 0,
|
|
182
|
+
max_completion_tokens: 384,
|
|
183
|
+
}, signal ? { signal } : undefined);
|
|
184
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new HealingError('LLM call timed out', 'network')), LLM_TIMEOUT_MS));
|
|
185
|
+
const completion = await Promise.race([completionPromise, timeoutPromise]);
|
|
186
|
+
if (completion.usage) {
|
|
187
|
+
const cached = completion.usage
|
|
188
|
+
.prompt_tokens_details?.cached_tokens ?? 0;
|
|
189
|
+
try {
|
|
190
|
+
const { recordCall } = await import('./cost-tracker.js');
|
|
191
|
+
recordCall({
|
|
192
|
+
promptTokens: completion.usage.prompt_tokens,
|
|
193
|
+
completionTokens: completion.usage.completion_tokens,
|
|
194
|
+
cachedPromptTokens: cached,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
// intentionally swallowed: cost-tracker is optional — heal must not fail if it's absent.
|
|
199
|
+
logWarn('best-effort cost-tracker recordCall failed:', err.message);
|
|
200
|
+
}
|
|
201
|
+
accumulateSessionCost(completion.usage);
|
|
202
|
+
// Ground-truth audit: dump raw OpenAI usage object per call.
|
|
203
|
+
try {
|
|
204
|
+
await logLlmCall({
|
|
205
|
+
model: ACTIVE_MODEL_ID,
|
|
206
|
+
endpoint: 'beta.chat.completions.parse',
|
|
207
|
+
caller: 'requestHealedLocator',
|
|
208
|
+
round: input.round,
|
|
209
|
+
raw_usage: completion.usage,
|
|
210
|
+
cached_tokens: cached,
|
|
211
|
+
fresh_input_tokens: completion.usage.prompt_tokens - cached,
|
|
212
|
+
local_cost_usd: ((completion.usage.prompt_tokens - cached) / 1e6) * PRICE_INPUT_FRESH_PER_M
|
|
213
|
+
+ (cached / 1e6) * PRICE_INPUT_CACHED_PER_M
|
|
214
|
+
+ (completion.usage.completion_tokens / 1e6) * PRICE_OUTPUT_PER_M,
|
|
215
|
+
prices_per_m: { input: PRICE_INPUT_FRESH_PER_M, cached: PRICE_INPUT_CACHED_PER_M, output: PRICE_OUTPUT_PER_M },
|
|
216
|
+
system_fingerprint: completion.system_fingerprint ?? null,
|
|
217
|
+
response_id: completion.id ?? null,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
// intentionally swallowed: audit log is best-effort — disk failures never break heal.
|
|
222
|
+
logWarn('best-effort llm-call audit log failed:', err.message);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const parsed = completion.choices[0].message.parsed;
|
|
226
|
+
if (!parsed)
|
|
227
|
+
throw new HealingError('LLM null/refusal', 'refusal');
|
|
228
|
+
recordCircuitSuccess();
|
|
229
|
+
return parsed.suggestions;
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
// Abort: surface immediately, no retries, no cost recorded.
|
|
233
|
+
const errName = err?.name ?? '';
|
|
234
|
+
if (errName === 'AbortError' || (signal && signal.aborted)) {
|
|
235
|
+
throw new HealingError('LLM call aborted by user', 'aborted');
|
|
236
|
+
}
|
|
237
|
+
if (err instanceof HealingError)
|
|
238
|
+
throw err;
|
|
239
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
240
|
+
if (errMsg.includes('Could not parse response content') || errMsg.includes('refusal')) {
|
|
241
|
+
throw new HealingError('LLM refusal', 'refusal');
|
|
242
|
+
}
|
|
243
|
+
const apiError = err;
|
|
244
|
+
if (apiError?.status === 401 || apiError?.status === 403) {
|
|
245
|
+
throw new HealingError(`OpenAI auth error (${apiError.status})`, 'auth');
|
|
246
|
+
}
|
|
247
|
+
lastErr = err;
|
|
248
|
+
if (attempt === 2) {
|
|
249
|
+
recordCircuitFailure();
|
|
250
|
+
throw new HealingError(`requestHealedLocator failed: ${String(err)}`, 'network');
|
|
251
|
+
}
|
|
252
|
+
await sleep(backoffWithJitter(attempt));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
throw new HealingError(`requestHealedLocator unreachable: ${String(lastErr)}`, 'network');
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=llm-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-client.js","sourceRoot":"","sources":["../../healing/llm-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* healing/logger.ts
|
|
3
|
+
*
|
|
4
|
+
* Level-gated logger for the HEAL framework.
|
|
5
|
+
*
|
|
6
|
+
* Level controlled by HEAL_LOG_LEVEL env (debug|info|warn|error).
|
|
7
|
+
* Default: warn. All output goes to stderr with a [HEAL] prefix.
|
|
8
|
+
* Unknown values fall back to warn.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { debug, info, warn, error } from './logger.js';
|
|
12
|
+
* warn('cache miss for', selector);
|
|
13
|
+
*/
|
|
14
|
+
export declare function debug(...args: unknown[]): void;
|
|
15
|
+
export declare function info(...args: unknown[]): void;
|
|
16
|
+
export declare function warn(...args: unknown[]): void;
|
|
17
|
+
export declare function error(...args: unknown[]): void;
|
|
18
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../healing/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA0BH,wBAAgB,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAwB;AACvE,wBAAgB,IAAI,CAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAwB;AACvE,wBAAgB,IAAI,CAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAwB;AACvE,wBAAgB,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAwB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* healing/logger.ts
|
|
3
|
+
*
|
|
4
|
+
* Level-gated logger for the HEAL framework.
|
|
5
|
+
*
|
|
6
|
+
* Level controlled by HEAL_LOG_LEVEL env (debug|info|warn|error).
|
|
7
|
+
* Default: warn. All output goes to stderr with a [HEAL] prefix.
|
|
8
|
+
* Unknown values fall back to warn.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { debug, info, warn, error } from './logger.js';
|
|
12
|
+
* warn('cache miss for', selector);
|
|
13
|
+
*/
|
|
14
|
+
const LEVEL_RANK = {
|
|
15
|
+
debug: 0,
|
|
16
|
+
info: 1,
|
|
17
|
+
warn: 2,
|
|
18
|
+
error: 3,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Resolve current minimum level from env. Re-read on every call so that
|
|
22
|
+
* tests can change HEAL_LOG_LEVEL between calls without module reload.
|
|
23
|
+
*/
|
|
24
|
+
function currentRank() {
|
|
25
|
+
const raw = (process.env.HEAL_LOG_LEVEL ?? 'warn').toLowerCase();
|
|
26
|
+
return LEVEL_RANK[raw] ?? LEVEL_RANK.warn;
|
|
27
|
+
}
|
|
28
|
+
function log(level, args) {
|
|
29
|
+
if (LEVEL_RANK[level] < currentRank())
|
|
30
|
+
return;
|
|
31
|
+
const msg = args.map(String).join(' ');
|
|
32
|
+
process.stderr.write(`[HEAL] ${level.toUpperCase()} ${msg}\n`);
|
|
33
|
+
}
|
|
34
|
+
export function debug(...args) { log('debug', args); }
|
|
35
|
+
export function info(...args) { log('info', args); }
|
|
36
|
+
export function warn(...args) { log('warn', args); }
|
|
37
|
+
export function error(...args) { log('error', args); }
|
|
38
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../healing/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,UAAU,GAA0B;IACxC,KAAK,EAAE,CAAC;IACR,IAAI,EAAG,CAAC;IACR,IAAI,EAAG,CAAC;IACR,KAAK,EAAE,CAAC;CACT,CAAC;AAEF;;;GAGG;AACH,SAAS,WAAW;IAClB,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,MAAM,CAAC,CAAC,WAAW,EAAW,CAAC;IAC1E,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED,SAAS,GAAG,CAAC,KAAY,EAAE,IAAe;IACxC,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,WAAW,EAAE;QAAE,OAAO;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAG,IAAe,IAAU,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AACvE,MAAM,UAAU,IAAI,CAAE,GAAG,IAAe,IAAU,GAAG,CAAC,MAAM,EAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AACvE,MAAM,UAAU,IAAI,CAAE,GAAG,IAAe,IAAU,GAAG,CAAC,MAAM,EAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AACvE,MAAM,UAAU,KAAK,CAAC,GAAG,IAAe,IAAU,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface ModelPricing {
|
|
2
|
+
/** OpenAI model id (the string passed as `model` in the API request). */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Short human-readable description. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Fresh input tokens — $/M. */
|
|
7
|
+
input: number;
|
|
8
|
+
/** Cached input tokens (prompt-cache hit) — $/M. */
|
|
9
|
+
cached: number;
|
|
10
|
+
/** Output (completion) tokens — $/M. */
|
|
11
|
+
output: number;
|
|
12
|
+
/** Family. Used only for the model picker UI. */
|
|
13
|
+
family: 'gpt-4.1' | 'gpt-5.4' | string;
|
|
14
|
+
/** Approximate ranking for the picker (lower = pick first when stronger needed). */
|
|
15
|
+
tier: 'flagship' | 'mini' | 'nano';
|
|
16
|
+
}
|
|
17
|
+
export declare const MODELS: Record<string, ModelPricing>;
|
|
18
|
+
export declare const DEFAULT_MODEL_ID = "gpt-4.1-mini";
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the active model from env (HEAL_MODEL) or fall back to default.
|
|
21
|
+
* If env var points at an unknown model id, log a warning and use default.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveModel(): ModelPricing;
|
|
24
|
+
/** List models in a deterministic order suitable for a picker UI. */
|
|
25
|
+
export declare function listModels(): ModelPricing[];
|
|
26
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../healing/models.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,oFAAoF;IACpF,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;CACpC;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAkD/C,CAAC;AAEF,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,YAAY,IAAI,YAAY,CAU3C;AA0BD,qEAAqE;AACrE,wBAAgB,UAAU,IAAI,YAAY,EAAE,CAM3C"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Model registry — pricing per million tokens (USD).
|
|
3
|
+
//
|
|
4
|
+
// Source of truth for which OpenAI model each heal call uses, and how its
|
|
5
|
+
// cost is computed. Selection is driven by env var HEAL_MODEL or by passing
|
|
6
|
+
// `model` in the heal call options. Default = gpt-4.1-mini.
|
|
7
|
+
//
|
|
8
|
+
// IMPORTANT: prices for gpt-5.4-* are placeholders. Update them when the
|
|
9
|
+
// official price sheet lands, or override at runtime via env vars:
|
|
10
|
+
// HEAL_PRICE_INPUT_PER_M, HEAL_PRICE_CACHED_PER_M, HEAL_PRICE_OUTPUT_PER_M
|
|
11
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
export const MODELS = {
|
|
13
|
+
// GPT-4.1 family — current production defaults.
|
|
14
|
+
'gpt-4.1': {
|
|
15
|
+
id: 'gpt-4.1',
|
|
16
|
+
label: 'Strong general / reasoning',
|
|
17
|
+
family: 'gpt-4.1',
|
|
18
|
+
tier: 'flagship',
|
|
19
|
+
input: 2.00,
|
|
20
|
+
cached: 0.50,
|
|
21
|
+
output: 8.00,
|
|
22
|
+
},
|
|
23
|
+
'gpt-4.1-mini': {
|
|
24
|
+
id: 'gpt-4.1-mini',
|
|
25
|
+
label: 'Cheap + fast gpt-4.1 (default)',
|
|
26
|
+
family: 'gpt-4.1',
|
|
27
|
+
tier: 'mini',
|
|
28
|
+
input: 0.40,
|
|
29
|
+
cached: 0.10,
|
|
30
|
+
output: 1.60,
|
|
31
|
+
},
|
|
32
|
+
'gpt-4.1-nano': {
|
|
33
|
+
id: 'gpt-4.1-nano',
|
|
34
|
+
label: 'Cheapest / fastest gpt-4.1',
|
|
35
|
+
family: 'gpt-4.1',
|
|
36
|
+
tier: 'nano',
|
|
37
|
+
input: 0.10,
|
|
38
|
+
cached: 0.025,
|
|
39
|
+
output: 0.40,
|
|
40
|
+
},
|
|
41
|
+
// GPT-5.4 family — newer. Pricing placeholders until official sheet lands;
|
|
42
|
+
// override via HEAL_PRICE_* env vars if needed.
|
|
43
|
+
'gpt-5.4-mini': {
|
|
44
|
+
id: 'gpt-5.4-mini',
|
|
45
|
+
label: 'Newer family, mini (placeholder pricing)',
|
|
46
|
+
family: 'gpt-5.4',
|
|
47
|
+
tier: 'mini',
|
|
48
|
+
input: 0.30,
|
|
49
|
+
cached: 0.075,
|
|
50
|
+
output: 1.20,
|
|
51
|
+
},
|
|
52
|
+
'gpt-5.4-nano': {
|
|
53
|
+
id: 'gpt-5.4-nano',
|
|
54
|
+
label: 'Newer family, smallest (placeholder pricing)',
|
|
55
|
+
family: 'gpt-5.4',
|
|
56
|
+
tier: 'nano',
|
|
57
|
+
input: 0.075,
|
|
58
|
+
cached: 0.019,
|
|
59
|
+
output: 0.30,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
export const DEFAULT_MODEL_ID = 'gpt-4.1-mini';
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the active model from env (HEAL_MODEL) or fall back to default.
|
|
65
|
+
* If env var points at an unknown model id, log a warning and use default.
|
|
66
|
+
*/
|
|
67
|
+
export function resolveModel() {
|
|
68
|
+
const fromEnv = process.env.HEAL_MODEL?.trim();
|
|
69
|
+
if (fromEnv && MODELS[fromEnv])
|
|
70
|
+
return applyPriceOverrides(MODELS[fromEnv]);
|
|
71
|
+
if (fromEnv) {
|
|
72
|
+
process.stderr.write(`[HEAL] Unknown model "${fromEnv}" in HEAL_MODEL — falling back to ${DEFAULT_MODEL_ID}.\n` +
|
|
73
|
+
`[HEAL] Known models: ${Object.keys(MODELS).join(', ')}\n`);
|
|
74
|
+
}
|
|
75
|
+
return applyPriceOverrides(MODELS[DEFAULT_MODEL_ID]);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Override pricing via env vars at runtime (useful when OpenAI's official
|
|
79
|
+
* price sheet shifts and you don't want to ship a new release).
|
|
80
|
+
*/
|
|
81
|
+
function applyPriceOverrides(m) {
|
|
82
|
+
const inp = numEnv('HEAL_PRICE_INPUT_PER_M');
|
|
83
|
+
const cch = numEnv('HEAL_PRICE_CACHED_PER_M');
|
|
84
|
+
const out = numEnv('HEAL_PRICE_OUTPUT_PER_M');
|
|
85
|
+
if (inp == null && cch == null && out == null)
|
|
86
|
+
return m;
|
|
87
|
+
return {
|
|
88
|
+
...m,
|
|
89
|
+
input: inp ?? m.input,
|
|
90
|
+
cached: cch ?? m.cached,
|
|
91
|
+
output: out ?? m.output,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function numEnv(key) {
|
|
95
|
+
const v = process.env[key];
|
|
96
|
+
if (v == null || v === '')
|
|
97
|
+
return undefined;
|
|
98
|
+
const n = Number(v);
|
|
99
|
+
return Number.isFinite(n) ? n : undefined;
|
|
100
|
+
}
|
|
101
|
+
/** List models in a deterministic order suitable for a picker UI. */
|
|
102
|
+
export function listModels() {
|
|
103
|
+
const order = [
|
|
104
|
+
'gpt-4.1-mini', 'gpt-4.1-nano', 'gpt-4.1',
|
|
105
|
+
'gpt-5.4-mini', 'gpt-5.4-nano',
|
|
106
|
+
];
|
|
107
|
+
return order.map(k => MODELS[k]).filter(Boolean);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../healing/models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { DockState } from './dock.html.js';
|
|
2
|
+
export type OverlayInbound = {
|
|
3
|
+
type: 'pick';
|
|
4
|
+
html: string;
|
|
5
|
+
xpath: string;
|
|
6
|
+
} | {
|
|
7
|
+
type: 'pick-error';
|
|
8
|
+
reason: 'shadow-unsupported';
|
|
9
|
+
xpath: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'pick-preview-continue';
|
|
12
|
+
} | {
|
|
13
|
+
type: 'pick-preview-repick';
|
|
14
|
+
} | {
|
|
15
|
+
type: 'intent';
|
|
16
|
+
text: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'accept';
|
|
19
|
+
} | {
|
|
20
|
+
type: 'reject';
|
|
21
|
+
} | {
|
|
22
|
+
type: 'edit';
|
|
23
|
+
} | {
|
|
24
|
+
type: 'cancel';
|
|
25
|
+
} | {
|
|
26
|
+
type: 'broaden';
|
|
27
|
+
} | {
|
|
28
|
+
type: 'force-llm';
|
|
29
|
+
} | {
|
|
30
|
+
type: 'cancel-llm';
|
|
31
|
+
};
|
|
32
|
+
export type OverlayOutbound = {
|
|
33
|
+
type: 'setState';
|
|
34
|
+
state: DockState;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'enablePickMode';
|
|
37
|
+
} | {
|
|
38
|
+
type: 'disablePickMode';
|
|
39
|
+
} | {
|
|
40
|
+
type: 'highlight';
|
|
41
|
+
xpath: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'destroy';
|
|
44
|
+
};
|
|
45
|
+
export declare const BINDING_NAME = "__healOverlay_send";
|
|
46
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../../healing/overlay/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,uBAAuB,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC;AAE3B,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB,eAAO,MAAM,YAAY,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../../healing/overlay/bridge.ts"],"names":[],"mappings":"AAuBA,MAAM,CAAC,MAAM,YAAY,GAAG,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const DOCK_CSS = "\n:host { all: initial; }\n:host *, :host *::before, :host *::after { box-sizing: border-box; }\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Heal Dock \u2014 refined dark dev panel. Muted palette, single cyan accent,\n readable for long sessions. Comfortable contrast, never strident.\n Fixed 380\u00D7560, 4 sticky zones.\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n:host {\n /* Color tokens \u2014 VS Code-style slate-blue dark theme.\n Background pulled off pure black to ease eye strain. */\n --bg-base: #1e293b; /* slate-800 \u2014 main panel bg */\n --bg-surface: #293548; /* head + footer */\n --bg-elevated: #334155; /* slate-700 \u2014 buttons */\n --bg-inset: #172033; /* code blocks, textarea */\n\n --border-subtle: #3b4456;\n --border-medium: #475569; /* slate-600 */\n --border-strong: #64748b; /* slate-500 */\n\n --fg-primary: #f1f5f9; /* slate-100 \u2014 primary text */\n --fg-secondary: #cbd5e1; /* slate-300 */\n --fg-muted: #94a3b8; /* slate-400 */\n --fg-faint: #64748b; /* slate-500 */\n\n --accent: #60a5fa; /* blue-400 \u2014 VS Code-style blue */\n --accent-dim: #3b82f6;\n --accent-bg: rgba(96, 165, 250, 0.10);\n --accent-border: rgba(96, 165, 250, 0.32);\n\n --success: #86efac; /* emerald-300 */\n --success-bg: rgba(134, 239, 172, 0.10);\n --success-border: rgba(134, 239, 172, 0.30);\n\n --warn: #fde68a; /* amber-200 \u2014 failing-line highlight */\n --warn-bg: rgba(253, 230, 138, 0.08);\n --warn-border: rgba(253, 230, 138, 0.36);\n\n --danger: #fca5a5; /* rose-300 */\n --danger-bg: rgba(252, 165, 165, 0.10);\n --danger-border: rgba(252, 165, 165, 0.32);\n\n --shadow-soft: 0 2px 4px rgba(15,23,42,0.3), 0 8px 24px rgba(15,23,42,0.4);\n --shadow-card: 0 0 0 1px rgba(255,255,255,0.03), 0 4px 16px rgba(15,23,42,0.5);\n}\n\n.dock {\n position: fixed; bottom: 16px; right: 16px;\n width: 380px; height: 580px;\n display: grid;\n grid-template-rows: auto auto auto 1fr auto;\n background: var(--bg-base);\n border: 1px solid var(--border-subtle);\n border-radius: 8px;\n box-shadow: var(--shadow-soft);\n font-family: \"JetBrains Mono\", \"IBM Plex Mono\", ui-monospace, Menlo, monospace;\n color: var(--fg-primary);\n z-index: 2147483647;\n overflow: hidden;\n font-feature-settings: \"ss01\",\"ss02\",\"cv11\";\n font-variant-ligatures: contextual;\n}\n.dock.dragging { opacity: 0.94; }\n.dock.err { border-color: var(--danger-border); }\n\n/* \u2500\u2500\u2500 Zone 1 \u2014 status bar \u2500\u2500\u2500 */\n.statusbar {\n display: flex; align-items: center; gap: 8px;\n padding: 7px 14px; height: 28px;\n background: var(--bg-inset);\n border-bottom: 1px solid var(--border-subtle);\n font-size: 10px; letter-spacing: .1em;\n color: var(--fg-muted);\n border-radius: 8px 8px 0 0;\n}\n.statusbar .sb-title {\n color: var(--accent); font-weight: 600;\n letter-spacing: .18em; text-transform: uppercase;\n}\n.statusbar .sb-sep { color: var(--fg-faint); }\n.statusbar .sb-state {\n color: var(--fg-primary); font-weight: 500;\n letter-spacing: .12em; text-transform: uppercase;\n}\n.statusbar .sb-cost { color: var(--accent); margin-left: auto; }\n.statusbar .sb-calls { color: var(--fg-secondary); }\n.statusbar #heal-elapsed { color: var(--warn); }\n.statusbar .sb-model {\n color: var(--accent); font-size: 9px;\n font-weight: 500; letter-spacing: .06em; text-transform: none;\n}\n.statusbar-day {\n height: 22px;\n background: rgba(96, 165, 250, 0.04);\n border-bottom: 1px solid var(--border-subtle);\n font-size: 9px; letter-spacing: .12em;\n border-radius: 0;\n}\n.statusbar-day .sb-day-label {\n color: var(--fg-muted); font-weight: 600; letter-spacing: .22em;\n}\n.statusbar-day .sb-cost { color: var(--accent); margin-left: 0; }\n\n/* \u2500\u2500\u2500 Zone 2 \u2014 head / drag handle \u2500\u2500\u2500 */\n.head {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 14px; height: 42px;\n background: var(--bg-surface);\n border-bottom: 1px solid var(--border-subtle);\n cursor: move; user-select: none;\n}\n.dot {\n width: 8px; height: 8px;\n background: var(--accent);\n border-radius: 50%;\n box-shadow: 0 0 6px rgba(96, 165, 250, 0.55);\n}\n.dock.err .dot {\n background: var(--danger);\n box-shadow: 0 0 6px rgba(252, 165, 165, 0.55);\n}\n.title {\n font-weight: 600; font-size: 11px;\n letter-spacing: .2em; text-transform: uppercase;\n color: var(--fg-primary);\n}\n.stage {\n margin-left: auto;\n font-size: 10px; letter-spacing: .14em; text-transform: uppercase;\n color: var(--fg-muted);\n display: inline-flex; gap: 6px; align-items: center;\n}\n.stage b { color: var(--accent); font-weight: 600; }\n.drag-grip {\n margin-left: 4px;\n color: var(--fg-faint); font-size: 10px;\n letter-spacing: 1px;\n}\n\n/* \u2500\u2500\u2500 Zone 3 \u2014 body (scrollable) \u2500\u2500\u2500 */\n.body {\n padding: 14px;\n overflow-y: auto; overflow-x: hidden;\n display: grid; gap: 12px; align-content: start;\n background: var(--bg-base);\n scrollbar-width: thin;\n scrollbar-color: var(--border-medium) transparent;\n}\n.body::-webkit-scrollbar { width: 6px; }\n.body::-webkit-scrollbar-track { background: transparent; }\n.body::-webkit-scrollbar-thumb {\n background: var(--border-medium);\n border-radius: 3px;\n}\n.body::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }\n\n/* \u2500\u2500\u2500 Zone 4 \u2014 footer (sticky actions) \u2500\u2500\u2500 */\n.footer {\n display: flex; gap: 8px; flex-wrap: wrap;\n padding: 12px 14px;\n background: var(--bg-surface);\n border-top: 1px solid var(--border-subtle);\n border-radius: 0 0 8px 8px;\n}\n\n/* \u2500\u2500\u2500 Rows / labels / values \u2500\u2500\u2500 */\n.row { display: grid; gap: 6px; }\n.label {\n font-size: 9px; letter-spacing: .22em; text-transform: uppercase;\n color: var(--fg-muted); font-weight: 600;\n}\n.value {\n font-size: 12px; line-height: 1.55;\n color: var(--fg-primary); word-break: break-all;\n}\n.value.code {\n background: var(--bg-inset);\n border: 1px solid var(--border-subtle);\n border-radius: 4px;\n padding: 8px 10px;\n font-size: 12px; line-height: 1.5;\n color: var(--fg-primary);\n}\n.value.code .ok { color: var(--success); }\n.value.code .bad { color: var(--danger); }\n.value.code.dim { opacity: 0.6; font-size: 11px; }\n.value.code.code-block {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--accent);\n border-left: 2px solid var(--accent);\n background: var(--bg-inset);\n font-weight: 500;\n line-height: 1.6;\n}\n\n/* \u2500\u2500\u2500 Failed-line block (only pick + intent) \u2500\u2500\u2500 */\n.failed-line {\n display: grid; gap: 10px;\n padding: 12px;\n background: var(--bg-surface);\n border: 1px solid var(--border-subtle);\n border-left: 2px solid var(--accent);\n border-radius: 4px;\n}\n.fl-head {\n display: flex; align-items: center; gap: 8px;\n font-size: 9px; letter-spacing: .22em; text-transform: uppercase;\n}\n.fl-tag {\n color: var(--accent); font-weight: 700;\n border: 1px solid var(--accent-border);\n background: var(--accent-bg);\n padding: 2px 6px;\n border-radius: 3px;\n}\n.fl-loc {\n color: var(--fg-muted); font-family: inherit;\n text-transform: none; letter-spacing: 0; font-size: 10px;\n margin-left: auto; word-break: break-all; text-align: right;\n font-weight: 400;\n}\n.fl-code {\n font-family: inherit; font-size: 12px; font-weight: 500;\n color: var(--warn);\n background: var(--warn-bg);\n border-left: 2px solid var(--warn-border);\n padding: 8px 10px; line-height: 1.55;\n border-radius: 0 3px 3px 0;\n white-space: pre-wrap; word-break: break-word;\n}\n.fl-meta { display: grid; gap: 6px; }\n.fl-meta-row {\n display: flex; align-items: baseline; gap: 10px;\n flex-wrap: wrap; font-size: 11px;\n}\n.fl-meta-label {\n font-size: 9px; letter-spacing: .22em; text-transform: uppercase;\n color: var(--fg-muted); font-weight: 600;\n min-width: 60px;\n}\n.fl-mono {\n font-family: inherit; font-size: 11px;\n color: var(--danger);\n background: var(--danger-bg);\n padding: 2px 6px;\n border-radius: 3px;\n word-break: break-all;\n}\n.fl-desc {\n font-size: 11px;\n color: var(--fg-primary);\n}\n\n/* \u2500\u2500\u2500 Intent textarea \u2500\u2500\u2500 */\ntextarea.intent {\n width: 100%; min-height: 100px; resize: vertical;\n background: var(--bg-inset);\n border: 1px solid var(--border-subtle);\n border-left: 2px solid var(--accent);\n border-radius: 4px;\n color: var(--fg-primary);\n font-family: inherit; font-size: 12px; line-height: 1.6;\n padding: 10px 12px;\n outline: none; caret-color: var(--accent);\n transition: border-color 120ms ease, background 120ms ease;\n}\ntextarea.intent::placeholder { color: var(--fg-faint); }\ntextarea.intent:focus {\n border-color: var(--accent);\n background: var(--bg-elevated);\n}\n\n/* \u2500\u2500\u2500 CTA / hints \u2500\u2500\u2500 */\n.cta {\n border: 1px dashed var(--border-medium);\n padding: 18px 12px; text-align: center;\n font-size: 11px; letter-spacing: .18em; text-transform: uppercase;\n color: var(--accent); font-weight: 600;\n background: var(--accent-bg);\n border-radius: 4px;\n}\n.hint {\n font-size: 10px; color: var(--fg-muted); line-height: 1.55;\n}\n\n/* \u2500\u2500\u2500 Keys / buttons \u2500\u2500\u2500 */\n.key, button.key {\n display: inline-flex; align-items: center; gap: 7px;\n font-family: inherit; font-size: 10px;\n letter-spacing: .14em; text-transform: uppercase;\n color: var(--fg-secondary);\n background: var(--bg-elevated);\n border: 1px solid var(--border-subtle);\n border-radius: 4px;\n padding: 7px 11px;\n cursor: pointer;\n transition: all 120ms ease;\n font-weight: 500;\n}\nbutton.key:hover {\n color: var(--fg-primary);\n border-color: var(--border-strong);\n background: var(--bg-surface);\n}\nbutton.key:active { transform: translateY(0.5px); }\nbutton.key:focus { outline: none; }\nbutton.key:focus-visible {\n outline: 1px solid var(--accent);\n outline-offset: 2px;\n}\n\nbutton.key.accept {\n color: var(--success);\n border-color: var(--success-border);\n}\nbutton.key.accept:hover {\n background: var(--success-bg);\n border-color: var(--success);\n color: var(--fg-primary);\n}\nbutton.key.accept .kbd {\n color: var(--bg-base); background: var(--success);\n border-color: var(--success);\n font-weight: 700;\n}\n\nbutton.key.reject {\n color: var(--danger);\n border-color: var(--danger-border);\n}\nbutton.key.reject:hover {\n background: var(--danger-bg);\n border-color: var(--danger);\n color: var(--fg-primary);\n}\nbutton.key.reject .kbd {\n color: var(--danger); border-color: var(--danger-border);\n}\n\nbutton.key:disabled {\n opacity: 0.4; cursor: not-allowed;\n}\nbutton.key:disabled:hover {\n background: var(--bg-elevated);\n border-color: var(--border-subtle);\n color: var(--fg-secondary);\n}\n\n.kbd {\n display: inline-block;\n min-width: 22px; padding: 2px 6px;\n font-size: 9px; font-weight: 600;\n text-align: center;\n border: 1px solid var(--border-medium);\n background: var(--bg-inset);\n color: var(--fg-primary);\n border-radius: 3px;\n letter-spacing: 0;\n}\n\n/* \u2500\u2500\u2500 Chips / pills \u2500\u2500\u2500 */\n.chip {\n display: inline-flex; align-items: center; gap: 4px;\n font-size: 9px; letter-spacing: .16em; text-transform: uppercase;\n padding: 2px 6px;\n border: 1px solid var(--success-border);\n background: var(--success-bg);\n color: var(--success); font-weight: 600;\n margin-left: 6px;\n border-radius: 3px;\n}\n.chip.bad {\n color: var(--danger);\n border-color: var(--danger-border);\n background: var(--danger-bg);\n}\n\n/* \u2500\u2500\u2500 Busy state \u2500\u2500\u2500 */\n.dock.busy textarea.intent {\n opacity: 0.55; pointer-events: none;\n}\n.dock.busy button.key:not([data-act=\"cancel-llm\"]):not([data-act=\"cancel\"]) {\n opacity: 0.35; pointer-events: none;\n}\n.dot.pulse {\n animation: dot-pulse 1.4s ease-in-out infinite alternate;\n}\n@keyframes dot-pulse {\n from { box-shadow: 0 0 4px rgba(96, 165, 250, 0.35); opacity: 0.6; }\n to { box-shadow: 0 0 12px rgba(96, 165, 250, 0.85); opacity: 1; }\n}\n.busy-bar {\n position: absolute; left: 0; right: 0; bottom: 56px;\n height: 1px; background: var(--border-subtle); overflow: hidden;\n}\n.busy-fill {\n position: absolute; height: 100%; width: 30%;\n background: linear-gradient(90deg, transparent, var(--accent), transparent);\n animation: busy-slide 1.6s ease-in-out infinite;\n opacity: 0.6;\n}\n@keyframes busy-slide {\n 0% { left: -30%; }\n 100% { left: 100%; }\n}\n\n/* \u2500\u2500\u2500 Page-level highlight (target preview) \u2500\u2500\u2500 */\n.hl-outline {\n outline: 2px solid var(--accent) !important;\n outline-offset: 2px;\n background-color: var(--accent-bg) !important;\n}\n\n/* \u2500\u2500\u2500 Pick-preview pane \u2500\u2500\u2500 */\n.preview-header {\n font-size: 10px; letter-spacing: .22em; text-transform: uppercase;\n color: var(--accent); font-weight: 700;\n padding-bottom: 6px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.preview-table { display: grid; gap: 5px; }\n.preview-row {\n display: flex; align-items: baseline; gap: 8px;\n font-size: 11px; line-height: 1.5;\n}\n.preview-key {\n font-size: 9px; letter-spacing: .18em; text-transform: uppercase;\n color: var(--fg-muted); font-weight: 600;\n min-width: 110px; flex-shrink: 0;\n}\n.preview-val {\n color: var(--fg-primary); word-break: break-all;\n}\n.preview-val.code-inline {\n font-family: inherit; font-size: 11px;\n background: var(--bg-inset);\n border: 1px solid var(--border-subtle);\n border-radius: 3px;\n padding: 1px 5px;\n color: var(--accent);\n}\n.preview-val.dim { color: var(--fg-faint); font-style: italic; }\n";
|
|
2
|
+
//# sourceMappingURL=dock.css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dock.css.d.ts","sourceRoot":"","sources":["../../../healing/overlay/dock.css.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,63cA8bpB,CAAC"}
|