testchimp-runner-core 0.0.21 → 0.0.23

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 (146) hide show
  1. package/VISION_DIAGNOSTICS_IMPROVEMENTS.md +336 -0
  2. package/dist/credit-usage-service.d.ts +9 -0
  3. package/dist/credit-usage-service.d.ts.map +1 -1
  4. package/dist/credit-usage-service.js +20 -5
  5. package/dist/credit-usage-service.js.map +1 -1
  6. package/dist/execution-service.d.ts +7 -2
  7. package/dist/execution-service.d.ts.map +1 -1
  8. package/dist/execution-service.js +91 -36
  9. package/dist/execution-service.js.map +1 -1
  10. package/dist/index.d.ts +30 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +91 -26
  13. package/dist/index.js.map +1 -1
  14. package/dist/llm-facade.d.ts +64 -8
  15. package/dist/llm-facade.d.ts.map +1 -1
  16. package/dist/llm-facade.js +361 -109
  17. package/dist/llm-facade.js.map +1 -1
  18. package/dist/llm-provider.d.ts +39 -0
  19. package/dist/llm-provider.d.ts.map +1 -0
  20. package/dist/llm-provider.js +7 -0
  21. package/dist/llm-provider.js.map +1 -0
  22. package/dist/model-constants.d.ts +21 -0
  23. package/dist/model-constants.d.ts.map +1 -0
  24. package/dist/model-constants.js +24 -0
  25. package/dist/model-constants.js.map +1 -0
  26. package/dist/orchestrator/index.d.ts +8 -0
  27. package/dist/orchestrator/index.d.ts.map +1 -0
  28. package/dist/orchestrator/index.js +23 -0
  29. package/dist/orchestrator/index.js.map +1 -0
  30. package/dist/orchestrator/orchestrator-agent.d.ts +66 -0
  31. package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -0
  32. package/dist/orchestrator/orchestrator-agent.js +855 -0
  33. package/dist/orchestrator/orchestrator-agent.js.map +1 -0
  34. package/dist/orchestrator/tool-registry.d.ts +74 -0
  35. package/dist/orchestrator/tool-registry.d.ts.map +1 -0
  36. package/dist/orchestrator/tool-registry.js +131 -0
  37. package/dist/orchestrator/tool-registry.js.map +1 -0
  38. package/dist/orchestrator/tools/check-page-ready.d.ts +13 -0
  39. package/dist/orchestrator/tools/check-page-ready.d.ts.map +1 -0
  40. package/dist/orchestrator/tools/check-page-ready.js +72 -0
  41. package/dist/orchestrator/tools/check-page-ready.js.map +1 -0
  42. package/dist/orchestrator/tools/extract-data.d.ts +13 -0
  43. package/dist/orchestrator/tools/extract-data.d.ts.map +1 -0
  44. package/dist/orchestrator/tools/extract-data.js +84 -0
  45. package/dist/orchestrator/tools/extract-data.js.map +1 -0
  46. package/dist/orchestrator/tools/index.d.ts +10 -0
  47. package/dist/orchestrator/tools/index.d.ts.map +1 -0
  48. package/dist/orchestrator/tools/index.js +18 -0
  49. package/dist/orchestrator/tools/index.js.map +1 -0
  50. package/dist/orchestrator/tools/inspect-page.d.ts +13 -0
  51. package/dist/orchestrator/tools/inspect-page.d.ts.map +1 -0
  52. package/dist/orchestrator/tools/inspect-page.js +39 -0
  53. package/dist/orchestrator/tools/inspect-page.js.map +1 -0
  54. package/dist/orchestrator/tools/recall-history.d.ts +13 -0
  55. package/dist/orchestrator/tools/recall-history.d.ts.map +1 -0
  56. package/dist/orchestrator/tools/recall-history.js +64 -0
  57. package/dist/orchestrator/tools/recall-history.js.map +1 -0
  58. package/dist/orchestrator/tools/take-screenshot.d.ts +15 -0
  59. package/dist/orchestrator/tools/take-screenshot.d.ts.map +1 -0
  60. package/dist/orchestrator/tools/take-screenshot.js +112 -0
  61. package/dist/orchestrator/tools/take-screenshot.js.map +1 -0
  62. package/dist/orchestrator/types.d.ts +133 -0
  63. package/dist/orchestrator/types.d.ts.map +1 -0
  64. package/dist/orchestrator/types.js +28 -0
  65. package/dist/orchestrator/types.js.map +1 -0
  66. package/dist/playwright-mcp-service.d.ts +9 -0
  67. package/dist/playwright-mcp-service.d.ts.map +1 -1
  68. package/dist/playwright-mcp-service.js +20 -5
  69. package/dist/playwright-mcp-service.js.map +1 -1
  70. package/dist/progress-reporter.d.ts +97 -0
  71. package/dist/progress-reporter.d.ts.map +1 -0
  72. package/dist/progress-reporter.js +18 -0
  73. package/dist/progress-reporter.js.map +1 -0
  74. package/dist/prompts.d.ts +24 -0
  75. package/dist/prompts.d.ts.map +1 -1
  76. package/dist/prompts.js +593 -68
  77. package/dist/prompts.js.map +1 -1
  78. package/dist/providers/backend-proxy-llm-provider.d.ts +25 -0
  79. package/dist/providers/backend-proxy-llm-provider.d.ts.map +1 -0
  80. package/dist/providers/backend-proxy-llm-provider.js +76 -0
  81. package/dist/providers/backend-proxy-llm-provider.js.map +1 -0
  82. package/dist/providers/local-llm-provider.d.ts +21 -0
  83. package/dist/providers/local-llm-provider.d.ts.map +1 -0
  84. package/dist/providers/local-llm-provider.js +35 -0
  85. package/dist/providers/local-llm-provider.js.map +1 -0
  86. package/dist/scenario-service.d.ts +27 -1
  87. package/dist/scenario-service.d.ts.map +1 -1
  88. package/dist/scenario-service.js +48 -12
  89. package/dist/scenario-service.js.map +1 -1
  90. package/dist/scenario-worker-class.d.ts +39 -2
  91. package/dist/scenario-worker-class.d.ts.map +1 -1
  92. package/dist/scenario-worker-class.js +614 -86
  93. package/dist/scenario-worker-class.js.map +1 -1
  94. package/dist/script-utils.d.ts +2 -0
  95. package/dist/script-utils.d.ts.map +1 -1
  96. package/dist/script-utils.js +44 -4
  97. package/dist/script-utils.js.map +1 -1
  98. package/dist/types.d.ts +11 -0
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js.map +1 -1
  101. package/dist/utils/browser-utils.d.ts +20 -1
  102. package/dist/utils/browser-utils.d.ts.map +1 -1
  103. package/dist/utils/browser-utils.js +102 -51
  104. package/dist/utils/browser-utils.js.map +1 -1
  105. package/dist/utils/page-info-utils.d.ts +23 -4
  106. package/dist/utils/page-info-utils.d.ts.map +1 -1
  107. package/dist/utils/page-info-utils.js +174 -43
  108. package/dist/utils/page-info-utils.js.map +1 -1
  109. package/package.json +1 -2
  110. package/plandocs/HUMAN_LIKE_IMPROVEMENTS.md +642 -0
  111. package/plandocs/MULTI_AGENT_ARCHITECTURE_REVIEW.md +844 -0
  112. package/plandocs/ORCHESTRATOR_MVP_SUMMARY.md +539 -0
  113. package/plandocs/PHASE1_ABSTRACTION_COMPLETE.md +241 -0
  114. package/plandocs/PHASE1_FINAL_STATUS.md +210 -0
  115. package/plandocs/PLANNING_SESSION_SUMMARY.md +372 -0
  116. package/plandocs/SCRIPT_CLEANUP_FEATURE.md +201 -0
  117. package/plandocs/SCRIPT_GENERATION_ARCHITECTURE.md +364 -0
  118. package/plandocs/SELECTOR_IMPROVEMENTS.md +139 -0
  119. package/src/credit-usage-service.ts +23 -5
  120. package/src/execution-service.ts +152 -42
  121. package/src/index.ts +169 -26
  122. package/src/llm-facade.ts +500 -126
  123. package/src/llm-provider.ts +43 -0
  124. package/src/model-constants.ts +23 -0
  125. package/src/orchestrator/index.ts +33 -0
  126. package/src/orchestrator/orchestrator-agent.ts +1037 -0
  127. package/src/orchestrator/tool-registry.ts +182 -0
  128. package/src/orchestrator/tools/check-page-ready.ts +75 -0
  129. package/src/orchestrator/tools/extract-data.ts +92 -0
  130. package/src/orchestrator/tools/index.ts +11 -0
  131. package/src/orchestrator/tools/inspect-page.ts +42 -0
  132. package/src/orchestrator/tools/recall-history.ts +72 -0
  133. package/src/orchestrator/tools/take-screenshot.ts +128 -0
  134. package/src/orchestrator/types.ts +200 -0
  135. package/src/playwright-mcp-service.ts +23 -5
  136. package/src/progress-reporter.ts +109 -0
  137. package/src/prompts.ts +606 -69
  138. package/src/providers/backend-proxy-llm-provider.ts +91 -0
  139. package/src/providers/local-llm-provider.ts +38 -0
  140. package/src/scenario-service.ts +83 -13
  141. package/src/scenario-worker-class.ts +740 -72
  142. package/src/script-utils.ts +50 -5
  143. package/src/types.ts +13 -1
  144. package/src/utils/browser-utils.ts +123 -51
  145. package/src/utils/page-info-utils.ts +210 -53
  146. package/testchimp-runner-core-0.0.22.tgz +0 -0
@@ -115,7 +115,14 @@ export function addTestChimpComment(script: string, repairAdvice?: string, hasht
115
115
  */
116
116
  export function generateTestScript(
117
117
  testName: string,
118
- steps: Array<{ stepNumber: number; description: string; playwrightCommand?: string; success?: boolean }>,
118
+ steps: Array<{
119
+ stepNumber: number;
120
+ description: string;
121
+ playwrightCommand?: string;
122
+ playwrightCommands?: string[];
123
+ success?: boolean;
124
+ error?: string;
125
+ }>,
119
126
  repairAdvice?: string,
120
127
  hashtags?: string[]
121
128
  ): string {
@@ -134,10 +141,48 @@ export function generateTestScript(
134
141
 
135
142
  // Add steps
136
143
  for (const step of steps) {
137
- const status = step.success === false ? ' [FAILED]' : '';
138
- scriptLines.push(` // ${step.description}${status}`);
139
- if (step.playwrightCommand && step.success !== false) {
140
- scriptLines.push(` ${step.playwrightCommand}`);
144
+ // Check if step was skipped
145
+ const isSkipped = step.error?.includes('Skipped due to') ||
146
+ (step.success === false && (!step.playwrightCommand && (!step.playwrightCommands || step.playwrightCommands.length === 0)));
147
+
148
+ if (isSkipped) {
149
+ // Step was skipped due to previous failures
150
+ scriptLines.push(` // ${step.description} [SKIPPED]`);
151
+ } else if (step.success === false) {
152
+ // Step failed - only show command if this step actually attempted any commands
153
+ if (step.playwrightCommands && step.playwrightCommands.length > 0) {
154
+ // Failed after attempting commands - show them
155
+ scriptLines.push(` // ${step.description} [FAILED]`);
156
+ step.playwrightCommands.forEach((cmd: string) => {
157
+ scriptLines.push(` // Attempted: ${cmd}`);
158
+ });
159
+ } else if (step.playwrightCommand) {
160
+ // Single command attempted (backward compatibility)
161
+ scriptLines.push(` // ${step.description} [FAILED] - ${step.playwrightCommand}`);
162
+ } else {
163
+ // Failed without attempting any commands
164
+ scriptLines.push(` // ${step.description} [FAILED]`);
165
+ }
166
+ } else {
167
+ // Step succeeded or in progress
168
+ // Only add comment if step has commands to show
169
+ const hasCommands = (step.playwrightCommands && step.playwrightCommands.length > 0) || step.playwrightCommand;
170
+
171
+ if (hasCommands) {
172
+ scriptLines.push(` // ${step.description}`);
173
+
174
+ // Handle multiple commands per step
175
+ if (step.playwrightCommands && step.playwrightCommands.length > 0) {
176
+ // Multiple commands - output all of them
177
+ step.playwrightCommands.forEach((cmd: string) => {
178
+ scriptLines.push(` ${cmd}`);
179
+ });
180
+ } else if (step.playwrightCommand) {
181
+ // Single command (backward compatibility)
182
+ scriptLines.push(` ${step.playwrightCommand}`);
183
+ }
184
+ }
185
+ // If step has no commands, skip it entirely (work was done in previous step)
141
186
  }
142
187
  }
143
188
 
package/src/types.ts CHANGED
@@ -99,13 +99,25 @@ export interface ScenarioResponse {
99
99
  error?: string;
100
100
  }
101
101
 
102
+ /**
103
+ * Individual sub-action within a step
104
+ */
105
+ export interface SubAction {
106
+ command: string;
107
+ success: boolean;
108
+ error?: string;
109
+ retryCount: number;
110
+ }
111
+
102
112
  /**
103
113
  * Individual scenario step
104
114
  */
105
115
  export interface ScenarioStep {
106
116
  stepNumber: number;
107
117
  description: string;
108
- playwrightCommand?: string;
118
+ playwrightCommand?: string; // For backward compatibility - last successful command or aggregated command
119
+ playwrightCommands?: string[]; // Multiple commands for complex steps
120
+ subActions?: SubAction[]; // Detailed tracking of all sub-actions
109
121
  success?: boolean;
110
122
  error?: string;
111
123
  retryCount?: number;
@@ -8,14 +8,24 @@ const { build } = esbuild;
8
8
  * Initialize browser with Playwright configuration
9
9
  * @param playwrightConfig - JavaScript config file content (playwright.config.js)
10
10
  * @param headless - Override headless mode (optional)
11
+ * @param playwrightConfigFilePath - Path to playwright config file (optional)
12
+ * @param logger - Optional logger callback for capturing logs
11
13
  * @returns Browser, context, and page instances
12
14
  */
13
15
  export async function initializeBrowser(
14
16
  playwrightConfigContent?: string,
15
17
  headless?: boolean,
16
- playwrightConfigFilePath?: string
18
+ playwrightConfigFilePath?: string,
19
+ logger?: (message: string, level?: 'log' | 'error' | 'warn') => void
17
20
  ): Promise<{ browser: any; context: any; page: any }> {
18
- console.log('Initializing browser with Playwright');
21
+ const log = (message: string, level: 'log' | 'error' | 'warn' = 'log') => {
22
+ if (logger) {
23
+ logger(message, level);
24
+ }
25
+ // No console fallback - logs are routed to consumer
26
+ };
27
+
28
+ log('Initializing browser with Playwright');
19
29
 
20
30
  // Import Playwright modules dynamically
21
31
  const playwright = await import('playwright');
@@ -25,11 +35,11 @@ export async function initializeBrowser(
25
35
 
26
36
  // Use Playwright config content if provided
27
37
  if (playwrightConfigContent) {
28
- console.log('Using provided Playwright config content');
38
+ log('Using provided Playwright config content');
29
39
 
30
40
  try {
31
41
  // Transpile the config content in-memory and evaluate it
32
- console.log(`Transpiling config content (${playwrightConfigContent.length} characters)`);
42
+ log(`Transpiling config content (${playwrightConfigContent.length} characters)`);
33
43
 
34
44
  const result = await build({
35
45
  stdin: {
@@ -55,25 +65,25 @@ export async function initializeBrowser(
55
65
  }
56
66
 
57
67
  const transpiledCode = result.outputFiles[0].text;
58
- console.log(`Transpilation complete. Generated ${transpiledCode.length} characters of code`);
68
+ log(`Transpilation complete. Generated ${transpiledCode.length} characters of code`);
59
69
 
60
70
  // Evaluate the transpiled code in a safe context
61
71
  const loadedConfig = eval(transpiledCode);
62
- console.log('Loaded config object:', loadedConfig);
63
- console.log('Config type:', typeof loadedConfig);
64
- console.log('Config keys:', loadedConfig ? Object.keys(loadedConfig) : 'null/undefined');
72
+ log(`Loaded config object: ${JSON.stringify(loadedConfig)}`);
73
+ log(`Config type: ${typeof loadedConfig}`);
74
+ log(`Config keys: ${loadedConfig ? Object.keys(loadedConfig).join(', ') : 'null/undefined'}`);
65
75
 
66
76
  // Get the actual config from the default export (ES module transpiled to CommonJS)
67
77
  const actualConfig = loadedConfig.default || loadedConfig;
68
78
 
69
79
  if (actualConfig && typeof actualConfig === 'object') {
70
- console.log('Successfully loaded Playwright config from content');
71
- console.log(`Config keys: ${Object.keys(actualConfig).join(', ')}`);
80
+ log('Successfully loaded Playwright config from content');
81
+ log(`Config keys: ${Object.keys(actualConfig).join(', ')}`);
72
82
 
73
83
  // Extract context options from the config
74
84
  if (actualConfig.use && actualConfig.use.contextOptions) {
75
85
  contextOptions = { ...contextOptions, ...actualConfig.use.contextOptions };
76
- console.log('Applied context options from config');
86
+ log('Applied context options from config');
77
87
  }
78
88
 
79
89
  // Apply other config settings
@@ -81,113 +91,113 @@ export async function initializeBrowser(
81
91
  // Override headless mode if specified in config and not explicitly overridden
82
92
  if (headless === undefined) {
83
93
  headless = actualConfig.use.headless;
84
- console.log(`Using headless mode from config: ${headless}`);
94
+ log(`Using headless mode from config: ${headless}`);
85
95
  }
86
96
  }
87
97
 
88
98
  // Apply timeout settings
89
99
  if (actualConfig.use && actualConfig.use.actionTimeout) {
90
- console.log(`Using action timeout from config: ${actualConfig.use.actionTimeout}ms`);
100
+ log(`Using action timeout from config: ${actualConfig.use.actionTimeout}ms`);
91
101
  }
92
102
 
93
103
  // Apply viewport settings
94
104
  if (actualConfig.use && actualConfig.use.viewport) {
95
105
  contextOptions.viewport = actualConfig.use.viewport;
96
- console.log(`Using viewport from config: ${JSON.stringify(actualConfig.use.viewport)}`);
106
+ log(`Using viewport from config: ${JSON.stringify(actualConfig.use.viewport)}`);
97
107
  }
98
108
 
99
109
  // Apply user agent
100
110
  if (actualConfig.use && actualConfig.use.userAgent) {
101
111
  contextOptions.userAgent = actualConfig.use.userAgent;
102
- console.log(`Using user agent from config: ${actualConfig.use.userAgent}`);
112
+ log(`Using user agent from config: ${actualConfig.use.userAgent}`);
103
113
  }
104
114
 
105
115
  // Apply other context options
106
116
  if (actualConfig.use && actualConfig.use.extraHTTPHeaders) {
107
117
  contextOptions.extraHTTPHeaders = actualConfig.use.extraHTTPHeaders;
108
- console.log(`Using extra HTTP headers from config`);
118
+ log(`Using extra HTTP headers from config`);
109
119
  }
110
120
 
111
121
  if (actualConfig.use && actualConfig.use.locale) {
112
122
  contextOptions.locale = actualConfig.use.locale;
113
- console.log(`Using locale from config: ${actualConfig.use.locale}`);
123
+ log(`Using locale from config: ${actualConfig.use.locale}`);
114
124
  }
115
125
 
116
126
  if (actualConfig.use && actualConfig.use.timezoneId) {
117
127
  contextOptions.timezoneId = actualConfig.use.timezoneId;
118
- console.log(`Using timezone from config: ${actualConfig.use.timezoneId}`);
128
+ log(`Using timezone from config: ${actualConfig.use.timezoneId}`);
119
129
  }
120
130
 
121
131
  if (actualConfig.use && actualConfig.use.geolocation) {
122
132
  contextOptions.geolocation = actualConfig.use.geolocation;
123
- console.log(`Using geolocation from config: ${JSON.stringify(actualConfig.use.geolocation)}`);
133
+ log(`Using geolocation from config: ${JSON.stringify(actualConfig.use.geolocation)}`);
124
134
  }
125
135
 
126
136
  if (actualConfig.use && actualConfig.use.permissions) {
127
137
  contextOptions.permissions = actualConfig.use.permissions;
128
- console.log(`Using permissions from config: ${JSON.stringify(actualConfig.use.permissions)}`);
138
+ log(`Using permissions from config: ${JSON.stringify(actualConfig.use.permissions)}`);
129
139
  }
130
140
 
131
141
  if (actualConfig.use && actualConfig.use.colorScheme) {
132
142
  contextOptions.colorScheme = actualConfig.use.colorScheme;
133
- console.log(`Using color scheme from config: ${actualConfig.use.colorScheme}`);
143
+ log(`Using color scheme from config: ${actualConfig.use.colorScheme}`);
134
144
  }
135
145
 
136
146
  if (actualConfig.use && actualConfig.use.reducedMotion) {
137
147
  contextOptions.reducedMotion = actualConfig.use.reducedMotion;
138
- console.log(`Using reduced motion from config: ${actualConfig.use.reducedMotion}`);
148
+ log(`Using reduced motion from config: ${actualConfig.use.reducedMotion}`);
139
149
  }
140
150
 
141
151
  if (actualConfig.use && actualConfig.use.forcedColors) {
142
152
  contextOptions.forcedColors = actualConfig.use.forcedColors;
143
- console.log(`Using forced colors from config: ${actualConfig.use.forcedColors}`);
153
+ log(`Using forced colors from config: ${actualConfig.use.forcedColors}`);
144
154
  }
145
155
 
146
156
  if (actualConfig.use && actualConfig.use.acceptDownloads) {
147
157
  contextOptions.acceptDownloads = actualConfig.use.acceptDownloads;
148
- console.log(`Using accept downloads from config: ${actualConfig.use.acceptDownloads}`);
158
+ log(`Using accept downloads from config: ${actualConfig.use.acceptDownloads}`);
149
159
  }
150
160
 
151
161
  if (actualConfig.use && actualConfig.use.bypassCSP) {
152
162
  contextOptions.bypassCSP = actualConfig.use.bypassCSP;
153
- console.log(`Using bypass CSP from config: ${actualConfig.use.bypassCSP}`);
163
+ log(`Using bypass CSP from config: ${actualConfig.use.bypassCSP}`);
154
164
  }
155
165
 
156
166
  if (actualConfig.use && actualConfig.use.javaScriptEnabled) {
157
167
  contextOptions.javaScriptEnabled = actualConfig.use.javaScriptEnabled;
158
- console.log(`Using JavaScript enabled from config: ${actualConfig.use.javaScriptEnabled}`);
168
+ log(`Using JavaScript enabled from config: ${actualConfig.use.javaScriptEnabled}`);
159
169
  }
160
170
 
161
171
  if (actualConfig.use && actualConfig.use.ignoreHTTPSErrors) {
162
172
  contextOptions.ignoreHTTPSErrors = actualConfig.use.ignoreHTTPSErrors;
163
- console.log(`Using ignore HTTPS errors from config: ${actualConfig.use.ignoreHTTPSErrors}`);
173
+ log(`Using ignore HTTPS errors from config: ${actualConfig.use.ignoreHTTPSErrors}`);
164
174
  }
165
175
 
166
- console.log(`Final context options: ${JSON.stringify(contextOptions, null, 2)}`);
176
+ log(`Final context options: ${JSON.stringify(contextOptions, null, 2)}`);
167
177
  } else {
168
- console.log('Config loaded but no valid configuration found');
178
+ log('Config loaded but no valid configuration found');
169
179
  }
170
180
  } catch (error) {
171
- console.error(`Error transpiling config content: ${error}`);
172
- console.log('Falling back to default configuration');
181
+ log(`Error transpiling config content: ${error}`, 'error');
182
+ log('Falling back to default configuration');
173
183
  }
174
184
  } else if (playwrightConfigFilePath) {
175
185
  // Fallback to file path if no content provided (for backward compatibility)
176
- console.log('No config content provided, falling back to file path');
186
+ log('No config content provided, falling back to file path');
177
187
  // Resolve the path - it might be relative or absolute
178
188
  const resolvedPath = path.isAbsolute(String(playwrightConfigFilePath))
179
189
  ? String(playwrightConfigFilePath)
180
190
  : path.resolve(String(process.cwd()), String(playwrightConfigFilePath));
181
191
 
182
- console.log(`Looking for Playwright config at: ${resolvedPath}`);
183
- console.log(`File exists: ${fs.existsSync(String(resolvedPath))}`);
192
+ log(`Looking for Playwright config at: ${resolvedPath}`);
193
+ log(`File exists: ${fs.existsSync(String(resolvedPath))}`);
184
194
 
185
195
  if (fs.existsSync(String(resolvedPath))) {
186
- console.log(`Loading Playwright config from: ${resolvedPath}`);
196
+ log(`Loading Playwright config from: ${resolvedPath}`);
187
197
 
188
198
  try {
189
199
  // Transpile the config in-memory and evaluate it
190
- console.log(`Transpiling config in-memory from: ${resolvedPath}`);
200
+ log(`Transpiling config in-memory from: ${resolvedPath}`);
191
201
 
192
202
  const result = await build({
193
203
  entryPoints: [String(resolvedPath)],
@@ -210,50 +220,50 @@ export async function initializeBrowser(
210
220
  }
211
221
 
212
222
  const transpiledCode = result.outputFiles[0].text;
213
- console.log(`Transpilation complete. Generated ${transpiledCode.length} characters of code`);
223
+ log(`Transpilation complete. Generated ${transpiledCode.length} characters of code`);
214
224
 
215
225
  // Evaluate the transpiled code in a safe context
216
226
  const loadedConfig = eval(transpiledCode);
217
- console.log('Loaded config object:', loadedConfig);
218
- console.log('Config type:', typeof loadedConfig);
219
- console.log('Config keys:', loadedConfig ? Object.keys(loadedConfig) : 'null/undefined');
227
+ log(`Loaded config object: ${JSON.stringify(loadedConfig)}`);
228
+ log(`Config type: ${typeof loadedConfig}`);
229
+ log(`Config keys: ${loadedConfig ? Object.keys(loadedConfig).join(', ') : 'null/undefined'}`);
220
230
 
221
231
  // Get the actual config from the default export (ES module transpiled to CommonJS)
222
232
  const actualConfig = loadedConfig.default || loadedConfig;
223
- console.log('Actual config:', actualConfig);
224
- console.log('Actual config keys:', actualConfig ? Object.keys(actualConfig) : 'null/undefined');
233
+ log(`Actual config: ${JSON.stringify(actualConfig)}`);
234
+ log(`Actual config keys: ${actualConfig ? Object.keys(actualConfig).join(', ') : 'null/undefined'}`);
225
235
 
226
236
  if (!actualConfig) {
227
- console.log('Config import did not return a valid config; using defaults');
237
+ log('Config import did not return a valid config; using defaults');
228
238
  } else {
229
239
  // Apply global use options
230
240
  if (actualConfig.use) {
231
241
  contextOptions = { ...actualConfig.use };
232
- console.log('Applied context options from Playwright config:', contextOptions);
242
+ log('Applied context options from Playwright config:', contextOptions);
233
243
  } else {
234
- console.log('No use property found in config');
244
+ log('No use property found in config');
235
245
  }
236
246
  // Apply first project overrides if present
237
247
  if (Array.isArray(actualConfig.projects) && actualConfig.projects.length > 0) {
238
248
  const firstProject = actualConfig.projects[0];
239
249
  if (firstProject && firstProject.use) {
240
250
  contextOptions = { ...contextOptions, ...firstProject.use };
241
- console.log('Applied project-specific options:', firstProject.use);
251
+ log('Applied project-specific options:', firstProject.use);
242
252
  }
243
253
  } else {
244
- console.log('No projects found in config');
254
+ log('No projects found in config');
245
255
  }
246
256
  }
247
257
 
248
258
  // No cleanup needed - we used in-memory transpilation
249
259
 
250
260
  } catch (error) {
251
- console.log(`Failed to load Playwright config via esbuild/import: ${error}`);
252
- console.log('Using default browser settings');
261
+ log(`Failed to load Playwright config via esbuild/import: ${error}`);
262
+ log('Using default browser settings');
253
263
  }
254
264
  } else {
255
- console.log(`Playwright config file not found at: ${resolvedPath}`);
256
- console.log('Using default browser settings');
265
+ log(`Playwright config file not found at: ${resolvedPath}`);
266
+ log('Using default browser settings');
257
267
  }
258
268
  }
259
269
 
@@ -274,3 +284,65 @@ export async function initializeBrowser(
274
284
 
275
285
  return { browser, context, page };
276
286
  }
287
+
288
+ /**
289
+ * Capture an optimized screenshot for vision API calls
290
+ * - Sets temporary viewport to reduce image size
291
+ * - Uses JPEG compression
292
+ * - Returns base64 data URL
293
+ *
294
+ * @param page - Playwright page instance
295
+ * @param options - Screenshot options
296
+ * @param log - Optional logger callback
297
+ * @returns Data URL string (data:image/jpeg;base64,...)
298
+ */
299
+ export async function captureOptimizedScreenshot(
300
+ page: any,
301
+ options: {
302
+ quality?: number; // JPEG quality (0-100), default 60 (optimal for vision API)
303
+ width?: number; // Viewport width, default 800
304
+ height?: number; // Viewport height, default 800
305
+ timeout?: number; // Screenshot timeout in ms, default 10000
306
+ } = {},
307
+ log?: (message: string) => void
308
+ ): Promise<string> {
309
+ const {
310
+ quality = 60, // Sweet spot: 50% smaller than quality 85, minimal visual loss
311
+ width = 800,
312
+ height = 800,
313
+ timeout = 10000
314
+ } = options;
315
+
316
+ // Save current viewport
317
+ const currentViewport = page.viewportSize();
318
+
319
+ // Set smaller viewport to reduce screenshot size
320
+ await page.setViewportSize({ width, height });
321
+
322
+ try {
323
+ const screenshotBuffer = await page.screenshot({
324
+ type: 'jpeg', // JPEG for smaller file size
325
+ quality, // Compression quality
326
+ fullPage: false, // Viewport only
327
+ timeout // Timeout for slow pages
328
+ });
329
+
330
+ // Convert Buffer to base64 string explicitly
331
+ const screenshotBase64 = screenshotBuffer.toString('base64');
332
+
333
+ if (log) {
334
+ const sizeKB = Math.round(screenshotBase64.length * 0.75 / 1024);
335
+ log(` 📏 Screenshot size: ~${sizeKB}KB (base64)`);
336
+ }
337
+
338
+ // Construct data URL for vision API
339
+ const imageDataUrl = `data:image/jpeg;base64,${screenshotBase64}`;
340
+
341
+ return imageDataUrl;
342
+ } finally {
343
+ // Always restore original viewport, even if screenshot fails
344
+ if (currentViewport) {
345
+ await page.setViewportSize(currentViewport);
346
+ }
347
+ }
348
+ }