testchimp-runner-core 0.0.1

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 (74) hide show
  1. package/dist/auth-config.d.ts +33 -0
  2. package/dist/auth-config.d.ts.map +1 -0
  3. package/dist/auth-config.js +69 -0
  4. package/dist/auth-config.js.map +1 -0
  5. package/dist/env-loader.d.ts +20 -0
  6. package/dist/env-loader.d.ts.map +1 -0
  7. package/dist/env-loader.js +83 -0
  8. package/dist/env-loader.js.map +1 -0
  9. package/dist/execution-service.d.ts +61 -0
  10. package/dist/execution-service.d.ts.map +1 -0
  11. package/dist/execution-service.js +822 -0
  12. package/dist/execution-service.js.map +1 -0
  13. package/dist/file-handler.d.ts +59 -0
  14. package/dist/file-handler.d.ts.map +1 -0
  15. package/dist/file-handler.js +75 -0
  16. package/dist/file-handler.js.map +1 -0
  17. package/dist/index.d.ts +46 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +196 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/llm-facade.d.ts +101 -0
  22. package/dist/llm-facade.d.ts.map +1 -0
  23. package/dist/llm-facade.js +289 -0
  24. package/dist/llm-facade.js.map +1 -0
  25. package/dist/playwright-mcp-service.d.ts +42 -0
  26. package/dist/playwright-mcp-service.d.ts.map +1 -0
  27. package/dist/playwright-mcp-service.js +167 -0
  28. package/dist/playwright-mcp-service.js.map +1 -0
  29. package/dist/prompts.d.ts +34 -0
  30. package/dist/prompts.d.ts.map +1 -0
  31. package/dist/prompts.js +237 -0
  32. package/dist/prompts.js.map +1 -0
  33. package/dist/scenario-service.d.ts +25 -0
  34. package/dist/scenario-service.d.ts.map +1 -0
  35. package/dist/scenario-service.js +119 -0
  36. package/dist/scenario-service.js.map +1 -0
  37. package/dist/scenario-worker-class.d.ts +30 -0
  38. package/dist/scenario-worker-class.d.ts.map +1 -0
  39. package/dist/scenario-worker-class.js +263 -0
  40. package/dist/scenario-worker-class.js.map +1 -0
  41. package/dist/script-utils.d.ts +44 -0
  42. package/dist/script-utils.d.ts.map +1 -0
  43. package/dist/script-utils.js +100 -0
  44. package/dist/script-utils.js.map +1 -0
  45. package/dist/types.d.ts +171 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +28 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/utils/browser-utils.d.ts +13 -0
  50. package/dist/utils/browser-utils.d.ts.map +1 -0
  51. package/dist/utils/browser-utils.js +269 -0
  52. package/dist/utils/browser-utils.js.map +1 -0
  53. package/dist/utils/page-info-utils.d.ts +16 -0
  54. package/dist/utils/page-info-utils.d.ts.map +1 -0
  55. package/dist/utils/page-info-utils.js +77 -0
  56. package/dist/utils/page-info-utils.js.map +1 -0
  57. package/env.prod +1 -0
  58. package/env.staging +1 -0
  59. package/package.json +38 -0
  60. package/src/auth-config.ts +84 -0
  61. package/src/env-loader.ts +91 -0
  62. package/src/execution-service.ts +999 -0
  63. package/src/file-handler.ts +104 -0
  64. package/src/index.ts +205 -0
  65. package/src/llm-facade.ts +413 -0
  66. package/src/playwright-mcp-service.ts +203 -0
  67. package/src/prompts.ts +247 -0
  68. package/src/scenario-service.ts +138 -0
  69. package/src/scenario-worker-class.ts +330 -0
  70. package/src/script-utils.ts +109 -0
  71. package/src/types.ts +202 -0
  72. package/src/utils/browser-utils.ts +272 -0
  73. package/src/utils/page-info-utils.ts +93 -0
  74. package/tsconfig.json +19 -0
@@ -0,0 +1,330 @@
1
+ import { Browser, Page, BrowserContext } from 'playwright';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { getEnhancedPageInfo } from './utils/page-info-utils';
5
+ import { initializeBrowser } from './utils/browser-utils';
6
+ import { LLMFacade } from './llm-facade';
7
+ import { ScenarioRunJob, ScenarioResponse, ScenarioStep } from './types';
8
+ import { FileHandler } from './file-handler';
9
+ import { AuthConfig } from './auth-config';
10
+ import { generateTestScript } from './script-utils';
11
+
12
+ // Define a simple logging interface for compatibility
13
+ interface OutputChannel {
14
+ appendLine: (text: string) => void;
15
+ }
16
+
17
+ // Legacy interface for backward compatibility
18
+ interface ScenarioJob {
19
+ id: string;
20
+ scenario: string;
21
+ testName?: string;
22
+ playwrightConfig?: string;
23
+ model?: string;
24
+ scenarioFileName?: string;
25
+ }
26
+
27
+
28
+ const MAX_RETRIES_PER_STEP = 2;
29
+
30
+ export class ScenarioWorker {
31
+ private initialized = false;
32
+ private sessionId: string | null = null;
33
+ private llmFacade: LLMFacade;
34
+ private fileHandler?: FileHandler;
35
+ private outputChannel?: OutputChannel;
36
+
37
+ constructor(fileHandler?: FileHandler, authConfig?: AuthConfig, outputChannel?: OutputChannel) {
38
+ this.llmFacade = new LLMFacade(authConfig);
39
+ this.fileHandler = fileHandler;
40
+ this.outputChannel = outputChannel;
41
+ }
42
+
43
+ private log(message: string): void {
44
+ console.log(message);
45
+ if (this.outputChannel) {
46
+ this.outputChannel.appendLine(`[ScenarioWorker] ${message}`);
47
+ }
48
+ }
49
+
50
+ private logError(message: string): void {
51
+ console.error(message);
52
+ if (this.outputChannel) {
53
+ this.outputChannel.appendLine(`[ScenarioWorker] ERROR: ${message}`);
54
+ }
55
+ }
56
+
57
+ async initialize(): Promise<void> {
58
+ try {
59
+ this.log('Initializing Scenario worker...');
60
+ this.sessionId = `scenario_worker_${Date.now()}`;
61
+ this.initialized = true;
62
+ this.log(`Scenario worker initialized with session: ${this.sessionId}`);
63
+ } catch (error) {
64
+ this.logError(`Scenario worker initialization error: ${error}`);
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ async processScenarioJob(job: ScenarioJob): Promise<ScenarioResponse> {
70
+ if (!this.initialized) {
71
+ throw new Error('Scenario worker not initialized');
72
+ }
73
+
74
+ const startTime = Date.now();
75
+ const steps: ScenarioStep[] = [];
76
+ let generatedScript = '';
77
+ let scriptPath: string | undefined;
78
+ let browser: Browser | undefined;
79
+ let context: BrowserContext | undefined;
80
+ let page: Page | undefined;
81
+ let overallSuccess = true;
82
+
83
+ try {
84
+ // 1. Break down scenario into steps using LLM
85
+ const scenarioSteps = await this.llmFacade.breakdownScenario(job.scenario, job.model);
86
+ steps.push(...scenarioSteps);
87
+
88
+ // 2. Start a new browser session using centralized utility
89
+ // Default to headed mode (headless: false) for better debugging
90
+ const browserInstance = await initializeBrowser(job.playwrightConfig, false);
91
+ browser = browserInstance.browser;
92
+ context = browserInstance.context;
93
+ page = browserInstance.page;
94
+
95
+ // Set reasonable timeout for all operations
96
+ page.setDefaultTimeout(5000); // 5 seconds
97
+
98
+ let previousSteps: ScenarioStep[] = [];
99
+ let lastError: string | undefined;
100
+
101
+ // 3. Execute each step
102
+ for (let i = 0; i < steps.length; i++) {
103
+ const step = steps[i];
104
+ step.stepNumber = i + 1;
105
+ step.retryCount = 0;
106
+
107
+ let stepSuccess = false;
108
+ let stepOutput = '';
109
+ let stepError: string | undefined;
110
+
111
+ for (let attempt = 0; attempt <= MAX_RETRIES_PER_STEP; attempt++) {
112
+ let currentAttemptCommand: string | undefined;
113
+ let currentAttemptSuccess = false;
114
+ let currentAttemptError: string | undefined;
115
+ const attemptTimestamp = Date.now();
116
+
117
+ try {
118
+ this.log(`Attempt ${attempt + 1} for step: ${step.description}`);
119
+
120
+ // Get current page state using Playwright's accessibility tree
121
+ const domSnapshot = {
122
+ url: page.url(),
123
+ title: await page.title(),
124
+ accessibilityTree: await page.accessibility.snapshot()
125
+ };
126
+
127
+ // Generate Playwright command using LLM
128
+ const pageInfo = await getEnhancedPageInfo(domSnapshot);
129
+ const command = await this.llmFacade.generatePlaywrightCommand(step.description, pageInfo, previousSteps, lastError, step, job.model);
130
+
131
+ if (!command) {
132
+ throw new Error('LLM failed to generate a Playwright command.');
133
+ }
134
+
135
+ step.playwrightCommand = command;
136
+ currentAttemptCommand = command;
137
+ this.log(` Command: ${command}`);
138
+
139
+ // Execute the command
140
+ await this.executePlaywrightCommand(page, browser, context, command);
141
+
142
+ stepSuccess = true;
143
+ currentAttemptSuccess = true;
144
+ stepOutput = `Executed: ${command}`;
145
+ stepError = undefined;
146
+ this.log(` ✅ SUCCESS: ${command}`);
147
+ break; // Step successful, move to next scenario step
148
+ } catch (error: any) {
149
+ stepError = error instanceof Error ? error.message : String(error);
150
+ currentAttemptError = stepError;
151
+ console.error(` ❌ FAILED (attempt ${attempt + 1}): ${stepError}`);
152
+ console.error(` Command attempted: ${currentAttemptCommand || 'N/A'}`);
153
+ step.retryCount++;
154
+
155
+ // Only update lastError if this is the final attempt
156
+ if (attempt === MAX_RETRIES_PER_STEP) {
157
+ lastError = stepError;
158
+ }
159
+
160
+ // If this is the last attempt, mark as failed and move on
161
+ if (attempt === MAX_RETRIES_PER_STEP) {
162
+ stepSuccess = false;
163
+ stepOutput = `Failed after ${MAX_RETRIES_PER_STEP + 1} attempts.`;
164
+ overallSuccess = false;
165
+ console.error(` 🚫 STEP FAILED after ${MAX_RETRIES_PER_STEP + 1} attempts`);
166
+ break; // Exit retry loop
167
+ }
168
+ } finally {
169
+ if (!step.attempts) {
170
+ step.attempts = [];
171
+ }
172
+ step.attempts.push({
173
+ attemptNumber: attempt + 1,
174
+ command: currentAttemptCommand,
175
+ success: currentAttemptSuccess,
176
+ error: currentAttemptError,
177
+ timestamp: attemptTimestamp
178
+ });
179
+ }
180
+ }
181
+
182
+ step.success = stepSuccess;
183
+ step.error = stepError;
184
+ previousSteps.push(step);
185
+ }
186
+
187
+ // Generate test name if not provided
188
+ const testName = job.testName || await this.llmFacade.generateTestName(job.scenario, job.model);
189
+
190
+ // Generate preferred filename
191
+ let preferredFileName: string;
192
+ if (testName && testName !== 'test') {
193
+ // Use the generated test name
194
+ const sanitizedName = testName
195
+ .replace(/[^a-zA-Z0-9\s-_]/g, '') // Remove special characters except spaces, hyphens, underscores
196
+ .replace(/\s+/g, '_') // Replace spaces with underscores
197
+ .replace(/_+/g, '_') // Replace multiple underscores with single underscore
198
+ .replace(/^_|_$/g, '') // Remove leading/trailing underscores
199
+ .toLowerCase();
200
+ preferredFileName = `${sanitizedName}.spec.js`;
201
+ } else {
202
+ // Use scenario file name if no meaningful test name was generated
203
+ const scenarioFileName = job.scenarioFileName || 'scenario';
204
+ const baseName = scenarioFileName.replace(/\.[^/.]+$/, ''); // Remove extension
205
+ const sanitizedName = baseName
206
+ .replace(/[^a-zA-Z0-9\s-_]/g, '') // Remove special characters except spaces, hyphens, underscores
207
+ .replace(/\s+/g, '_') // Replace spaces with underscores
208
+ .replace(/_+/g, '_') // Replace multiple underscores with single underscore
209
+ .replace(/^_|_$/g, '') // Remove leading/trailing underscores
210
+ .toLowerCase();
211
+ preferredFileName = `${sanitizedName}.spec.js`;
212
+ }
213
+
214
+ // Generate clean script with TestChimp comment and code
215
+ generatedScript = generateTestScript(testName, steps);
216
+
217
+ // Generate detailed execution log
218
+ const logLines: string[] = [];
219
+ logLines.push(`# Scenario Execution Log`);
220
+ logLines.push(`Job ID: ${job.id}`);
221
+ logLines.push(`Scenario: ${job.scenario}`);
222
+ logLines.push(`Start Time: ${new Date(startTime).toISOString()}`);
223
+ logLines.push(`End Time: ${new Date().toISOString()}`);
224
+ logLines.push(`Total Execution Time: ${Date.now() - startTime}ms`);
225
+ logLines.push(`Overall Success: ${overallSuccess ? 'YES' : 'NO'}`);
226
+ logLines.push(``);
227
+
228
+ for (const step of steps) {
229
+ logLines.push(`## Step ${step.stepNumber}: ${step.description}`);
230
+ logLines.push(`Status: ${step.success ? 'SUCCESS' : 'FAILED'}`);
231
+ logLines.push(`Retry Count: ${step.retryCount || 0}`);
232
+
233
+ if (step.playwrightCommand) {
234
+ logLines.push(`Final Command: ${step.playwrightCommand}`);
235
+ }
236
+
237
+ if (step.error) {
238
+ logLines.push(`Final Error: ${step.error}`);
239
+ }
240
+
241
+ if (step.attempts && step.attempts.length > 0) {
242
+ logLines.push(`### Attempts:`);
243
+ for (const attempt of step.attempts) {
244
+ logLines.push(`- Attempt ${attempt.attemptNumber}:`);
245
+ logLines.push(` Command: ${attempt.command || 'N/A'}`);
246
+ logLines.push(` Success: ${attempt.success ? 'YES' : 'NO'}`);
247
+ if (attempt.error) {
248
+ logLines.push(` Error: ${attempt.error}`);
249
+ }
250
+ logLines.push(` Timestamp: ${new Date(attempt.timestamp).toISOString()}`);
251
+ }
252
+ }
253
+
254
+ logLines.push(``);
255
+ }
256
+
257
+ const executionLog = logLines.join('\n');
258
+
259
+ return {
260
+ success: overallSuccess,
261
+ steps,
262
+ generatedScript,
263
+ executionLog,
264
+ executionTime: Date.now() - startTime,
265
+ testName,
266
+ preferredFileName
267
+ };
268
+
269
+ } catch (error: any) {
270
+ overallSuccess = false;
271
+ console.error('Overall scenario processing error:', error);
272
+ return {
273
+ success: false,
274
+ steps,
275
+ generatedScript,
276
+ executionLog: `# Scenario Execution Log\nJob ID: ${job.id}\nScenario: ${job.scenario}\nError: ${error instanceof Error ? error.message : 'Unknown error during scenario processing'}`,
277
+ executionTime: Date.now() - startTime,
278
+ testName: job.testName || 'test',
279
+ preferredFileName: 'test.spec.js',
280
+ error: error instanceof Error ? error.message : 'Unknown error during scenario processing'
281
+ };
282
+ } finally {
283
+ if (browser) {
284
+ await browser.close();
285
+ }
286
+ }
287
+ }
288
+
289
+
290
+
291
+
292
+
293
+
294
+ private async executePlaywrightCommand(
295
+ page: Page,
296
+ browser: Browser,
297
+ context: BrowserContext,
298
+ command: string
299
+ ): Promise<void> {
300
+ // Set reasonable timeouts
301
+ page.setDefaultTimeout(5000); // 5 seconds
302
+
303
+ try {
304
+ // Execute command directly without validation
305
+ const commandFunction = new Function('page', 'browser', 'context', 'expect', `
306
+ return (async () => {
307
+ try {
308
+ ${command}
309
+ } catch (error) {
310
+ console.error('Command execution error:', error);
311
+ throw error;
312
+ }
313
+ })();
314
+ `);
315
+
316
+ await commandFunction(page, browser, context, require('@playwright/test').expect);
317
+
318
+ } finally {
319
+ // Reset to default timeout
320
+ page.setDefaultTimeout(10000); // Reset to default 10 seconds
321
+ }
322
+ }
323
+
324
+
325
+
326
+ async cleanup(): Promise<void> {
327
+ this.initialized = false;
328
+ this.sessionId = null;
329
+ }
330
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Script Generation Utilities
3
+ *
4
+ * This module provides utilities for generating and formatting test scripts
5
+ * with TestChimp-specific markers and comments.
6
+ */
7
+
8
+ /**
9
+ * TestChimp managed test comment that should be added to all generated scripts
10
+ */
11
+ export const TESTCHIMP_MANAGED_COMMENT = `/*
12
+
13
+ This is a TestChimp Managed Test.
14
+
15
+ */`;
16
+
17
+ /**
18
+ * Generates TestChimp managed test comment with optional repair advice
19
+ * @param repairAdvice Optional repair advice to include in the comment
20
+ * @returns The complete comment block
21
+ */
22
+ export function generateTestChimpComment(repairAdvice?: string): string {
23
+ if (repairAdvice) {
24
+ return `/*
25
+
26
+ This is a TestChimp Managed Test.
27
+
28
+ Repair Advice:
29
+ ${repairAdvice}
30
+
31
+ */`;
32
+ }
33
+ return TESTCHIMP_MANAGED_COMMENT;
34
+ }
35
+
36
+ /**
37
+ * Adds the TestChimp managed test comment to the beginning of a script
38
+ * @param script The original script content
39
+ * @param repairAdvice Optional repair advice to include in the comment
40
+ * @returns The script with TestChimp comment prepended
41
+ */
42
+ export function addTestChimpComment(script: string, repairAdvice?: string): string {
43
+ // If the script already has the TestChimp comment, update it with repair advice if provided
44
+ if (script.includes('This is a TestChimp Managed Test')) {
45
+ if (repairAdvice) {
46
+ // Replace existing comment with new one that includes repair advice
47
+ const commentRegex = /\/\*[\s\S]*?This is a TestChimp Managed Test\.[\s\S]*?\*\//;
48
+ const newComment = generateTestChimpComment(repairAdvice);
49
+ return script.replace(commentRegex, newComment);
50
+ }
51
+ return script;
52
+ }
53
+
54
+ // Add the comment at the beginning of the script
55
+ const comment = generateTestChimpComment(repairAdvice);
56
+ return `${comment}\n\n${script}`;
57
+ }
58
+
59
+ /**
60
+ * Generates a complete test script with TestChimp comment, imports, and test structure
61
+ * @param testName The name of the test
62
+ * @param steps Array of test steps with descriptions and commands
63
+ * @param includeTestChimpComment Whether to include the TestChimp managed test comment
64
+ * @param repairAdvice Optional repair advice to include in the comment
65
+ * @returns The complete test script
66
+ */
67
+ export function generateTestScript(
68
+ testName: string,
69
+ steps: Array<{ stepNumber: number; description: string; playwrightCommand?: string; success?: boolean }>,
70
+ includeTestChimpComment: boolean = true,
71
+ repairAdvice?: string
72
+ ): string {
73
+ const scriptLines: string[] = [];
74
+
75
+ // Add TestChimp comment if requested
76
+ if (includeTestChimpComment) {
77
+ const comment = generateTestChimpComment(repairAdvice);
78
+ scriptLines.push(comment);
79
+ scriptLines.push('');
80
+ }
81
+
82
+ // Add imports
83
+ scriptLines.push(`import { test, expect } from '@playwright/test';`);
84
+
85
+ // Add test structure
86
+ scriptLines.push(`test('${testName.replace(/'/g, "\\'")}', async ({ page, browser, context }) => {`);
87
+
88
+ // Add steps
89
+ for (const step of steps) {
90
+ const status = step.success === false ? ' [FAILED]' : '';
91
+ scriptLines.push(` // Step ${step.stepNumber}: ${step.description}${status}`);
92
+ if (step.playwrightCommand && step.success !== false) {
93
+ scriptLines.push(` ${step.playwrightCommand}`);
94
+ }
95
+ }
96
+
97
+ scriptLines.push(`});`);
98
+
99
+ return scriptLines.join('\n');
100
+ }
101
+
102
+ /**
103
+ * Checks if a script is a TestChimp managed test
104
+ * @param script The script content to check
105
+ * @returns True if the script contains the TestChimp managed test comment
106
+ */
107
+ export function isTestChimpManagedTest(script: string): boolean {
108
+ return script.includes('This is a TestChimp Managed Test');
109
+ }
package/src/types.ts ADDED
@@ -0,0 +1,202 @@
1
+ // ============================================================================
2
+ // CORE TYPES
3
+ // ============================================================================
4
+
5
+ /**
6
+ * Playwright MCP configuration - JavaScript config file content (playwright.config.js)
7
+ */
8
+ export type PlaywrightConfig = string;
9
+
10
+ // ============================================================================
11
+ // SCRIPT EXECUTION TYPES
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Request structure for the Playwright script executor
16
+ */
17
+ export interface PlaywrightExecutionRequest {
18
+ /** Main Playwright script content */
19
+ script: string;
20
+ /** Optional pre-script to run before the main script */
21
+ prescript?: string;
22
+ /** Optional post-script to run after the main script */
23
+ postscript?: string;
24
+ /** Playwright configuration file content */
25
+ playwrightConfig: string;
26
+ /** Optional GPT model to use for AI operations */
27
+ model?: string;
28
+ }
29
+
30
+ /**
31
+ * Response structure for the Playwright script executor
32
+ */
33
+ export interface PlaywrightExecutionResponse {
34
+ /** Whether the execution was successful */
35
+ success: boolean;
36
+ /** Execution results from each script phase */
37
+ results: {
38
+ prescript?: ScriptResult;
39
+ script: ScriptResult;
40
+ postscript?: ScriptResult;
41
+ };
42
+ /** Overall execution time in milliseconds */
43
+ executionTime: number;
44
+ /** Any errors that occurred during execution */
45
+ error?: string;
46
+ }
47
+
48
+ /**
49
+ * Individual script execution result
50
+ */
51
+ export interface ScriptResult {
52
+ /** Whether this specific script executed successfully */
53
+ success: boolean;
54
+ /** Output from the script execution */
55
+ output: string;
56
+ /** Any errors from this script */
57
+ error?: string;
58
+ /** Execution time for this script in milliseconds */
59
+ executionTime: number;
60
+ }
61
+
62
+ // ============================================================================
63
+ // SCENARIO EXECUTION TYPES
64
+ // ============================================================================
65
+
66
+ /**
67
+ * Scenario execution request
68
+ */
69
+ export interface ScenarioRequest {
70
+ scenario: string;
71
+ testName?: string;
72
+ playwrightConfig?: PlaywrightConfig;
73
+ model?: string;
74
+ }
75
+
76
+ /**
77
+ * Scenario execution job for worker queue
78
+ */
79
+ export interface ScenarioRunJob {
80
+ id: string;
81
+ scenario: string;
82
+ testName?: string;
83
+ playwrightConfig?: PlaywrightConfig;
84
+ model?: string;
85
+ scenarioFileName?: string;
86
+ }
87
+
88
+ /**
89
+ * Scenario execution response
90
+ */
91
+ export interface ScenarioResponse {
92
+ success: boolean;
93
+ steps: ScenarioStep[];
94
+ generatedScript: string;
95
+ executionLog: string;
96
+ executionTime: number;
97
+ testName?: string;
98
+ preferredFileName?: string;
99
+ error?: string;
100
+ }
101
+
102
+ /**
103
+ * Individual scenario step
104
+ */
105
+ export interface ScenarioStep {
106
+ stepNumber: number;
107
+ description: string;
108
+ playwrightCommand?: string;
109
+ success?: boolean;
110
+ error?: string;
111
+ retryCount?: number;
112
+ attempts?: Array<{
113
+ attemptNumber: number;
114
+ command?: string;
115
+ success: boolean;
116
+ error?: string;
117
+ timestamp: number;
118
+ }>;
119
+ }
120
+
121
+ /**
122
+ * Legacy scenario job interface (for backward compatibility)
123
+ */
124
+ export interface ScenarioJob {
125
+ id: string;
126
+ scenario: string;
127
+ config?: PlaywrightConfig;
128
+ resolve: (result: ScenarioResponse) => void;
129
+ reject: (error: Error) => void;
130
+ }
131
+
132
+ // ============================================================================
133
+ // AI REPAIR TYPES
134
+ // ============================================================================
135
+
136
+ /**
137
+ * Execution mode for script execution
138
+ */
139
+ export enum ExecutionMode {
140
+ RUN_EXACTLY = 'RUN_EXACTLY',
141
+ RUN_WITH_AI_REPAIR = 'RUN_WITH_AI_REPAIR'
142
+ }
143
+
144
+ /**
145
+ * Script execution request with AI repair capabilities
146
+ */
147
+ export interface ScriptExecutionRequest {
148
+ script?: string; // Optional if scriptFilePath is provided
149
+ scriptFilePath?: string; // Path to script file (alternative to script content)
150
+ mode: ExecutionMode;
151
+ repair_flexibility?: number; // 0-5, defaults to 3
152
+ playwrightConfig?: PlaywrightConfig;
153
+ playwrightConfigFilePath?: string; // Path to playwright config file (alternative to playwrightConfig content)
154
+ model?: string;
155
+ headless?: boolean; // defaults to false (headed)
156
+ deflake_run_count?: number; // defaults to 1
157
+ }
158
+
159
+ /**
160
+ * Script execution response with repair information
161
+ */
162
+ export interface ScriptExecutionResponse {
163
+ run_status: 'success' | 'failed';
164
+ repair_status?: 'success' | 'failed' | 'partial';
165
+ repair_confidence?: number; // 0-5
166
+ repair_advice?: string;
167
+ updated_script?: string;
168
+ executionTime: number;
169
+ num_deflake_runs?: number; // Number of deflaking runs made (excluding original run)
170
+ error?: string;
171
+ }
172
+
173
+ /**
174
+ * Individual script step for AI repair
175
+ */
176
+ export interface ScriptStep {
177
+ description: string;
178
+ code: string;
179
+ success?: boolean;
180
+ error?: string;
181
+ }
182
+
183
+ /**
184
+ * Step operation types for AI repair
185
+ */
186
+ export enum StepOperation {
187
+ MODIFY = 'MODIFY',
188
+ INSERT = 'INSERT',
189
+ REMOVE = 'REMOVE'
190
+ }
191
+
192
+ /**
193
+ * Step repair action
194
+ */
195
+ export interface StepRepairAction {
196
+ operation: StepOperation;
197
+ stepIndex?: number; // For MODIFY and REMOVE operations
198
+ newStep?: ScriptStep; // For MODIFY and INSERT operations
199
+ insertAfterIndex?: number; // For INSERT operation
200
+ }
201
+
202
+ // Repair suggestion and confidence interfaces are now in llm-facade.ts