@fasttest-ai/qa-agent 0.4.2 → 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.
package/dist/cloud.d.ts DELETED
@@ -1,279 +0,0 @@
1
- /**
2
- * HTTP client to Cloud API — authenticates with the org's API key.
3
- */
4
- export interface CloudClientOptions {
5
- apiKey: string;
6
- baseUrl?: string;
7
- }
8
- export interface DeviceCodeResponse {
9
- code: string;
10
- poll_token: string;
11
- expires_in: number;
12
- verification_url: string;
13
- }
14
- export interface DeviceCodeStatusResponse {
15
- status: "pending" | "completed" | "expired";
16
- api_key?: string;
17
- org_name?: string;
18
- org_slug?: string;
19
- }
20
- export interface RunResponse {
21
- execution_id: string;
22
- status: string;
23
- base_url: string | null;
24
- test_cases: TestCasePayload[];
25
- previous_execution_id: string | null;
26
- /** Map of test_case_id → status from the previous execution (for fail-first ordering). */
27
- previous_statuses?: Record<string, string>;
28
- }
29
- export interface RegressionItem {
30
- test_case_id: string;
31
- name: string;
32
- previous_status: string;
33
- current_status: string;
34
- error?: string;
35
- }
36
- export interface ExecutionDiff {
37
- previous_execution_id: string | null;
38
- regressions: RegressionItem[];
39
- fixes: RegressionItem[];
40
- new_tests: Array<{
41
- test_case_id: string;
42
- name: string;
43
- status: string;
44
- }>;
45
- removed_tests: Array<{
46
- test_case_id: string;
47
- name: string;
48
- }>;
49
- unchanged: {
50
- passed: number;
51
- failed: number;
52
- };
53
- healed_count: number;
54
- }
55
- export interface TestCasePayload {
56
- id: string;
57
- name: string;
58
- steps: TestStep[];
59
- assertions: TestAssertion[];
60
- test_data: Record<string, unknown>[];
61
- timeout_seconds: number;
62
- retry_count: number;
63
- depends_on?: string[];
64
- }
65
- export interface TestStep {
66
- action: string;
67
- selector?: string;
68
- value?: string;
69
- url?: string;
70
- description?: string;
71
- /** Semantic intent — what this step is trying to accomplish (e.g. "Click the submit button"). */
72
- intent?: string;
73
- timeout?: number;
74
- fields?: Record<string, string>;
75
- condition?: string;
76
- key?: string;
77
- file_paths?: string[];
78
- expression?: string;
79
- target?: string;
80
- width?: number;
81
- height?: number;
82
- type?: string;
83
- text?: string;
84
- count?: number;
85
- attribute?: string;
86
- }
87
- export interface TestAssertion {
88
- type: string;
89
- selector?: string;
90
- text?: string;
91
- url?: string;
92
- count?: number;
93
- attribute?: string;
94
- value?: string;
95
- expected_value?: string;
96
- description?: string;
97
- }
98
- /** Thrown when the org has exceeded its monthly run quota (HTTP 402). */
99
- export declare class QuotaExceededError extends Error {
100
- plan: string;
101
- used: number;
102
- limit: number;
103
- constructor(plan: string, used: number, limit: number);
104
- }
105
- export declare class CloudClient {
106
- private apiKey;
107
- private baseUrl;
108
- constructor(options: CloudClientOptions);
109
- /**
110
- * Request a device code for browser-based authentication.
111
- * Unauthenticated — no API key needed.
112
- */
113
- static requestDeviceCode(baseUrl: string): Promise<DeviceCodeResponse>;
114
- /**
115
- * Poll for device code status. Returns when completed or expired.
116
- * Unauthenticated — uses the poll_token for identity.
117
- */
118
- static pollDeviceCode(baseUrl: string, pollToken: string): Promise<DeviceCodeStatusResponse>;
119
- private request;
120
- get<T>(path: string): Promise<T>;
121
- post<T>(path: string, body?: unknown): Promise<T>;
122
- health(): Promise<{
123
- status: string;
124
- }>;
125
- listProjects(): Promise<Record<string, unknown>>;
126
- /** Get-or-create a project by name. Returns project with id. */
127
- resolveProject(name: string, baseUrl?: string): Promise<{
128
- id: string;
129
- name: string;
130
- }>;
131
- /** List all test suites across all projects for the org. */
132
- listSuites(search?: string): Promise<Array<Record<string, unknown>>>;
133
- /** Resolve a suite by name (case-insensitive). Returns the best match. */
134
- resolveSuite(name: string, projectId?: string): Promise<{
135
- id: string;
136
- name: string;
137
- }>;
138
- /** Create a test suite under a project. */
139
- createSuite(projectId: string, body: {
140
- name: string;
141
- description?: string;
142
- test_type?: string;
143
- auto_generated?: boolean;
144
- tags?: string[];
145
- }): Promise<{
146
- id: string;
147
- name: string;
148
- }>;
149
- /** Create a test case and link it to suite(s). */
150
- createTestCase(body: {
151
- name: string;
152
- description?: string;
153
- priority?: string;
154
- steps: Array<Record<string, unknown>>;
155
- assertions: Array<Record<string, unknown>>;
156
- tags?: string[];
157
- test_suite_ids: string[];
158
- auto_generated?: boolean;
159
- generated_by_agent?: boolean;
160
- natural_language_source?: string;
161
- }): Promise<{
162
- id: string;
163
- name: string;
164
- }>;
165
- /** Update an existing test case. */
166
- updateTestCase(testCaseId: string, body: {
167
- name?: string;
168
- description?: string;
169
- priority?: string;
170
- steps?: Array<Record<string, unknown>>;
171
- assertions?: Array<Record<string, unknown>>;
172
- tags?: string[];
173
- }): Promise<{
174
- id: string;
175
- name: string;
176
- }>;
177
- /** Apply a healed selector to a test case, replacing the original in all steps. */
178
- applyHealing(testCaseId: string, originalSelector: string, healedSelector: string): Promise<Record<string, unknown>>;
179
- /** Detect repeated step sequences across test cases. */
180
- detectSharedSteps(projectId?: string, autoCreate?: boolean): Promise<Record<string, unknown>>;
181
- /** Resolve an environment by name within a suite's project. */
182
- resolveEnvironment(suiteId: string, name: string): Promise<{
183
- id: string;
184
- name: string;
185
- base_url: string;
186
- }>;
187
- /** Start a test run — returns execution_id + test cases to execute locally. */
188
- startRun(body: {
189
- suite_id: string;
190
- environment_id?: string;
191
- browser?: string;
192
- test_case_ids?: string[];
193
- }): Promise<RunResponse>;
194
- /** Report the result of a single test case. */
195
- reportResult(executionId: string, body: {
196
- test_case_id: string;
197
- status: string;
198
- duration_ms?: number;
199
- error_message?: string;
200
- screenshots?: string[];
201
- console_logs?: string[];
202
- step_results?: Record<string, unknown>[];
203
- retry_attempt?: number;
204
- network_summary?: Array<{
205
- url: string;
206
- method: string;
207
- status: number;
208
- duration: number;
209
- mimeType: string;
210
- responseSize: number;
211
- }>;
212
- }): Promise<{
213
- status: string;
214
- }>;
215
- /** Mark a suite execution as complete. */
216
- completeExecution(executionId: string, status?: string): Promise<Record<string, unknown>>;
217
- /** Cancel a running execution. */
218
- cancelExecution(executionId: string): Promise<Record<string, unknown>>;
219
- /** Get execution status. */
220
- getExecutionStatus(executionId: string): Promise<Record<string, unknown>>;
221
- /** Get regression diff comparing this execution against the previous run. */
222
- getExecutionDiff(executionId: string): Promise<ExecutionDiff>;
223
- /** Notify that a test case is about to start executing. Non-fatal. */
224
- notifyTestStarted(executionId: string, testCaseId: string, testCaseName: string): Promise<void>;
225
- /** Notify that selector healing is being attempted. Non-fatal. */
226
- notifyHealingStarted(executionId: string, testCaseId: string, originalSelector: string): Promise<void>;
227
- /** Lightweight check for control signals (pause/cancel) from the dashboard. */
228
- checkControlStatus(executionId: string): Promise<string>;
229
- /** Set the org's GitHub token. */
230
- setGithubToken(token: string): Promise<Record<string, unknown>>;
231
- /** Post test results as a PR comment. */
232
- postPrComment(body: {
233
- pr_url: string;
234
- execution_id: string;
235
- status: string;
236
- total: number;
237
- passed: number;
238
- failed: number;
239
- skipped: number;
240
- duration_seconds: number;
241
- test_results: Array<{
242
- name?: string;
243
- status?: string;
244
- error?: string;
245
- }>;
246
- healed?: Array<{
247
- original_selector?: string;
248
- new_selector?: string;
249
- strategy?: string;
250
- confidence?: number;
251
- }>;
252
- flaky_retries?: Array<{
253
- name?: string;
254
- retry_attempts?: number;
255
- }>;
256
- regressions?: Array<{
257
- name: string;
258
- previous_status: string;
259
- current_status: string;
260
- error?: string;
261
- }>;
262
- fixes?: Array<{
263
- name: string;
264
- previous_status: string;
265
- current_status: string;
266
- }>;
267
- }): Promise<Record<string, unknown>>;
268
- /** Save a chaos report with adversarial findings. */
269
- saveChaosReport(projectId: string | undefined, body: {
270
- url: string;
271
- findings: Array<{
272
- severity: string;
273
- category: string;
274
- description: string;
275
- reproduction_steps: string[];
276
- console_errors?: string[];
277
- }>;
278
- }): Promise<Record<string, unknown>>;
279
- }
package/dist/cloud.js DELETED
@@ -1,240 +0,0 @@
1
- /**
2
- * HTTP client to Cloud API — authenticates with the org's API key.
3
- */
4
- /** Thrown when the org has exceeded its monthly run quota (HTTP 402). */
5
- export class QuotaExceededError extends Error {
6
- plan;
7
- used;
8
- limit;
9
- constructor(plan, used, limit) {
10
- super(`Monthly run limit reached (${used}/${limit}). Current plan: ${plan}. Upgrade at https://fasttest.ai to continue.`);
11
- this.plan = plan;
12
- this.used = used;
13
- this.limit = limit;
14
- this.name = "QuotaExceededError";
15
- }
16
- }
17
- export class CloudClient {
18
- apiKey;
19
- baseUrl;
20
- constructor(options) {
21
- this.apiKey = options.apiKey;
22
- this.baseUrl = (options.baseUrl ?? "https://api.fasttest.ai").replace(/\/$/, "");
23
- }
24
- /**
25
- * Request a device code for browser-based authentication.
26
- * Unauthenticated — no API key needed.
27
- */
28
- static async requestDeviceCode(baseUrl) {
29
- const url = `${baseUrl.replace(/\/$/, "")}/api/v1/auth/device-code`;
30
- const resp = await fetch(url, { method: "POST" });
31
- if (!resp.ok) {
32
- const text = await resp.text();
33
- throw new Error(`Device code request failed (${resp.status}): ${text}`);
34
- }
35
- return (await resp.json());
36
- }
37
- /**
38
- * Poll for device code status. Returns when completed or expired.
39
- * Unauthenticated — uses the poll_token for identity.
40
- */
41
- static async pollDeviceCode(baseUrl, pollToken) {
42
- const url = `${baseUrl.replace(/\/$/, "")}/api/v1/auth/device-code/status?poll_token=${encodeURIComponent(pollToken)}`;
43
- const resp = await fetch(url);
44
- if (!resp.ok) {
45
- const text = await resp.text();
46
- throw new Error(`Device code poll failed (${resp.status}): ${text}`);
47
- }
48
- return (await resp.json());
49
- }
50
- async request(method, path, body) {
51
- const url = `${this.baseUrl}/api/v1${path}`;
52
- const headers = {
53
- "x-api-key": this.apiKey,
54
- "Content-Type": "application/json",
55
- };
56
- const maxRetries = 2;
57
- const baseDelay = 1000;
58
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
59
- const controller = new AbortController();
60
- const timeoutId = setTimeout(() => controller.abort(), 30_000);
61
- try {
62
- const init = { method, headers, signal: controller.signal };
63
- if (body !== undefined) {
64
- init.body = JSON.stringify(body);
65
- }
66
- const resp = await fetch(url, init);
67
- clearTimeout(timeoutId);
68
- if (!resp.ok) {
69
- const text = await resp.text();
70
- if (resp.status >= 500 && attempt < maxRetries) {
71
- await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));
72
- continue;
73
- }
74
- if (resp.status === 402) {
75
- // Parse "Monthly run limit reached (50/50). Current plan: free."
76
- const match = text.match(/\((\d+)\/(\d+)\).*plan:\s*(\w+)/i);
77
- throw new QuotaExceededError(match?.[3] ?? "unknown", match ? parseInt(match[1]) : 0, match ? parseInt(match[2]) : 0);
78
- }
79
- throw new Error(`Cloud API ${method} ${path} → ${resp.status}: ${text}`);
80
- }
81
- return (await resp.json());
82
- }
83
- catch (err) {
84
- clearTimeout(timeoutId);
85
- const isRetryable = err instanceof Error &&
86
- (err.name === "AbortError" || err.message.includes("fetch failed"));
87
- if (isRetryable && attempt < maxRetries) {
88
- await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));
89
- continue;
90
- }
91
- throw err;
92
- }
93
- }
94
- throw new Error(`Cloud API ${method} ${path}: max retries exceeded`);
95
- }
96
- async get(path) {
97
- return this.request("GET", path);
98
- }
99
- async post(path, body) {
100
- return this.request("POST", path, body);
101
- }
102
- // --- Health ---
103
- async health() {
104
- const url = `${this.baseUrl}/health`;
105
- const resp = await fetch(url);
106
- return (await resp.json());
107
- }
108
- // --- Projects ---
109
- async listProjects() {
110
- return this.get("/qa/projects/");
111
- }
112
- /** Get-or-create a project by name. Returns project with id. */
113
- async resolveProject(name, baseUrl) {
114
- const body = { name };
115
- if (baseUrl)
116
- body.base_url = baseUrl;
117
- return this.post("/qa/projects/resolve", body);
118
- }
119
- // --- Suites ---
120
- /** List all test suites across all projects for the org. */
121
- async listSuites(search) {
122
- const qs = search ? `?search=${encodeURIComponent(search)}` : "";
123
- return this.get(`/qa/projects/suites/all${qs}`);
124
- }
125
- /** Resolve a suite by name (case-insensitive). Returns the best match. */
126
- async resolveSuite(name, projectId) {
127
- const body = { name };
128
- if (projectId)
129
- body.project_id = projectId;
130
- return this.post("/qa/projects/suites/resolve", body);
131
- }
132
- // --- Suite + Test Case Management ---
133
- /** Create a test suite under a project. */
134
- async createSuite(projectId, body) {
135
- return this.post(`/qa/projects/${projectId}/test-suites`, {
136
- ...body,
137
- project_id: projectId,
138
- });
139
- }
140
- /** Create a test case and link it to suite(s). */
141
- async createTestCase(body) {
142
- return this.post("/qa/test-cases/", body);
143
- }
144
- /** Update an existing test case. */
145
- async updateTestCase(testCaseId, body) {
146
- return this.request("PUT", `/qa/test-cases/${testCaseId}`, body);
147
- }
148
- /** Apply a healed selector to a test case, replacing the original in all steps. */
149
- async applyHealing(testCaseId, originalSelector, healedSelector) {
150
- return this.post(`/qa/test-cases/${testCaseId}/apply-healing`, {
151
- original_selector: originalSelector,
152
- healed_selector: healedSelector,
153
- });
154
- }
155
- /** Detect repeated step sequences across test cases. */
156
- async detectSharedSteps(projectId, autoCreate) {
157
- const params = new URLSearchParams();
158
- if (projectId)
159
- params.set("project_id", projectId);
160
- if (autoCreate)
161
- params.set("auto_create", "true");
162
- const qs = params.toString() ? `?${params.toString()}` : "";
163
- return this.post(`/qa/shared-steps/detect${qs}`, {});
164
- }
165
- // --- Environments ---
166
- /** Resolve an environment by name within a suite's project. */
167
- async resolveEnvironment(suiteId, name) {
168
- return this.post("/qa/environments/resolve", { suite_id: suiteId, name });
169
- }
170
- // --- Execution (Phase 3) ---
171
- /** Start a test run — returns execution_id + test cases to execute locally. */
172
- async startRun(body) {
173
- return this.post("/qa/execution/run", body);
174
- }
175
- /** Report the result of a single test case. */
176
- async reportResult(executionId, body) {
177
- return this.post(`/qa/execution/executions/${executionId}/results`, body);
178
- }
179
- /** Mark a suite execution as complete. */
180
- async completeExecution(executionId, status) {
181
- return this.post(`/qa/execution/executions/${executionId}/complete`, { status });
182
- }
183
- /** Cancel a running execution. */
184
- async cancelExecution(executionId) {
185
- return this.post(`/qa/execution/executions/${executionId}/cancel`, {});
186
- }
187
- /** Get execution status. */
188
- async getExecutionStatus(executionId) {
189
- return this.get(`/qa/execution/executions/${executionId}`);
190
- }
191
- /** Get regression diff comparing this execution against the previous run. */
192
- async getExecutionDiff(executionId) {
193
- return this.get(`/qa/execution/executions/${executionId}/diff`);
194
- }
195
- /** Notify that a test case is about to start executing. Non-fatal. */
196
- async notifyTestStarted(executionId, testCaseId, testCaseName) {
197
- try {
198
- await this.post(`/qa/execution/executions/${executionId}/test-started`, {
199
- test_case_id: testCaseId,
200
- test_case_name: testCaseName,
201
- });
202
- }
203
- catch {
204
- // Non-fatal — War Room will just miss the "running" indicator
205
- }
206
- }
207
- /** Notify that selector healing is being attempted. Non-fatal. */
208
- async notifyHealingStarted(executionId, testCaseId, originalSelector) {
209
- try {
210
- await this.post(`/qa/execution/executions/${executionId}/healing-started`, {
211
- test_case_id: testCaseId,
212
- original_selector: originalSelector,
213
- });
214
- }
215
- catch {
216
- // Non-fatal — War Room will just miss the "healing..." indicator
217
- }
218
- }
219
- /** Lightweight check for control signals (pause/cancel) from the dashboard. */
220
- async checkControlStatus(executionId) {
221
- const resp = await this.get(`/qa/execution/executions/${executionId}/control-status`);
222
- return resp.status;
223
- }
224
- // --- GitHub (Phase 6) ---
225
- /** Set the org's GitHub token. */
226
- async setGithubToken(token) {
227
- return this.request("PUT", "/qa/github/token", { github_token: token });
228
- }
229
- /** Post test results as a PR comment. */
230
- async postPrComment(body) {
231
- return this.post("/qa/github/pr-comment", body);
232
- }
233
- // --- Chaos (Break My App) ---
234
- /** Save a chaos report with adversarial findings. */
235
- async saveChaosReport(projectId, body) {
236
- const qs = projectId ? `?project_id=${projectId}` : "";
237
- return this.post(`/qa/chaos/reports${qs}`, body);
238
- }
239
- }
240
- //# sourceMappingURL=cloud.js.map
package/dist/cloud.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cloud.js","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts DELETED
@@ -1,21 +0,0 @@
1
- /**
2
- * Global config manager — reads/writes ~/.fasttest/config.json
3
- * Stores API key and base URL so the MCP server can boot without CLI args.
4
- * Falls back to legacy ~/.qa-agent/config.json for backward compatibility.
5
- */
6
- declare const CONFIG_PATH: string;
7
- export interface GlobalConfig {
8
- api_key?: string;
9
- base_url?: string;
10
- }
11
- /**
12
- * Load config from ~/.fasttest/config.json (or legacy ~/.qa-agent/config.json).
13
- * Returns empty object if file doesn't exist or is malformed.
14
- */
15
- export declare function loadGlobalConfig(): GlobalConfig;
16
- /**
17
- * Merge new values into ~/.fasttest/config.json and write with 0o600 permissions.
18
- * Always writes to the new location (~/.fasttest/).
19
- */
20
- export declare function saveGlobalConfig(updates: Partial<GlobalConfig>): void;
21
- export { CONFIG_PATH };
package/dist/config.js DELETED
@@ -1,49 +0,0 @@
1
- /**
2
- * Global config manager — reads/writes ~/.fasttest/config.json
3
- * Stores API key and base URL so the MCP server can boot without CLI args.
4
- * Falls back to legacy ~/.qa-agent/config.json for backward compatibility.
5
- */
6
- import * as fs from "node:fs";
7
- import * as path from "node:path";
8
- import * as os from "node:os";
9
- const NEW_CONFIG_DIR = path.join(os.homedir(), ".fasttest");
10
- const OLD_CONFIG_DIR = path.join(os.homedir(), ".qa-agent");
11
- /** Resolve config dir: prefer ~/.fasttest/, fall back to ~/.qa-agent/ */
12
- function resolveConfigDir() {
13
- if (fs.existsSync(path.join(NEW_CONFIG_DIR, "config.json")))
14
- return NEW_CONFIG_DIR;
15
- if (fs.existsSync(path.join(OLD_CONFIG_DIR, "config.json")))
16
- return OLD_CONFIG_DIR;
17
- return NEW_CONFIG_DIR;
18
- }
19
- const CONFIG_DIR = resolveConfigDir();
20
- const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
21
- /**
22
- * Load config from ~/.fasttest/config.json (or legacy ~/.qa-agent/config.json).
23
- * Returns empty object if file doesn't exist or is malformed.
24
- */
25
- export function loadGlobalConfig() {
26
- if (!fs.existsSync(CONFIG_PATH))
27
- return {};
28
- try {
29
- return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
30
- }
31
- catch {
32
- return {};
33
- }
34
- }
35
- /**
36
- * Merge new values into ~/.fasttest/config.json and write with 0o600 permissions.
37
- * Always writes to the new location (~/.fasttest/).
38
- */
39
- export function saveGlobalConfig(updates) {
40
- const existing = loadGlobalConfig();
41
- const merged = { ...existing, ...updates };
42
- const saveDir = NEW_CONFIG_DIR;
43
- const savePath = path.join(saveDir, "config.json");
44
- fs.mkdirSync(saveDir, { recursive: true });
45
- const content = JSON.stringify(merged, null, 2) + "\n";
46
- fs.writeFileSync(savePath, content, { mode: 0o600 });
47
- }
48
- export { CONFIG_PATH };
49
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAC5D,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAE5D,yEAAyE;AACzE,SAAS,gBAAgB;IACvB,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;QAAE,OAAO,cAAc,CAAC;IACnF,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;QAAE,OAAO,cAAc,CAAC;IACnF,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;AACtC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAOzD;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAiB,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA8B;IAC7D,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,cAAc,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACnD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACvD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
package/dist/healer.d.ts DELETED
@@ -1,32 +0,0 @@
1
- /**
2
- * Self-healing module — locator repair cascade.
3
- *
4
- * When a test step fails with a selector error, the healer tries:
5
- * 1. data-testid (confidence: 98%)
6
- * 2. ARIA labels (confidence: 95%)
7
- * 3. Text content (confidence: 90%)
8
- * 4. Structural (confidence: 85%)
9
- * 5. AI-generated (confidence: 75%) — via Cloud LLM call
10
- *
11
- * After a successful heal the pattern is stored in the Cloud DB so
12
- * future failures with the same signature are fixed instantly.
13
- */
14
- import type { Page } from "playwright";
15
- import type { CloudClient } from "./cloud.js";
16
- export interface HealResult {
17
- healed: boolean;
18
- newSelector?: string;
19
- strategy?: string;
20
- confidence?: number;
21
- error?: string;
22
- }
23
- export interface ElementContext {
24
- action: string;
25
- description?: string;
26
- intent?: string;
27
- }
28
- /**
29
- * Attempt to heal a broken selector by trying 5 strategies in order.
30
- * Returns the first working selector with its confidence score.
31
- */
32
- export declare function healSelector(page: Page, cloud: CloudClient | null, originalSelector: string, failureType: string, errorMessage: string, pageUrl: string, context?: ElementContext): Promise<HealResult>;