auto-feedback 0.1.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/README.md +180 -0
- package/build/capture/console-collector.d.ts +16 -0
- package/build/capture/console-collector.js +43 -0
- package/build/capture/error-collector.d.ts +15 -0
- package/build/capture/error-collector.js +47 -0
- package/build/capture/network-collector.d.ts +16 -0
- package/build/capture/network-collector.js +76 -0
- package/build/capture/process-collector.d.ts +16 -0
- package/build/capture/process-collector.js +48 -0
- package/build/capture/types.d.ts +61 -0
- package/build/capture/types.js +5 -0
- package/build/index.d.ts +6 -0
- package/build/index.js +41 -0
- package/build/interaction/selectors.d.ts +26 -0
- package/build/interaction/selectors.js +84 -0
- package/build/interaction/types.d.ts +56 -0
- package/build/interaction/types.js +5 -0
- package/build/process/cleanup.d.ts +23 -0
- package/build/process/cleanup.js +50 -0
- package/build/process/launcher.d.ts +22 -0
- package/build/process/launcher.js +54 -0
- package/build/process/monitor.d.ts +14 -0
- package/build/process/monitor.js +67 -0
- package/build/process/types.d.ts +84 -0
- package/build/process/types.js +5 -0
- package/build/screenshot/auto-capture.d.ts +14 -0
- package/build/screenshot/auto-capture.js +38 -0
- package/build/screenshot/capture.d.ts +21 -0
- package/build/screenshot/capture.js +48 -0
- package/build/screenshot/optimize.d.ts +19 -0
- package/build/screenshot/optimize.js +28 -0
- package/build/screenshot/types.d.ts +43 -0
- package/build/screenshot/types.js +4 -0
- package/build/server.d.ts +10 -0
- package/build/server.js +18 -0
- package/build/session-manager.d.ts +119 -0
- package/build/session-manager.js +284 -0
- package/build/tools/check-port.d.ts +10 -0
- package/build/tools/check-port.js +40 -0
- package/build/tools/click-element.d.ts +13 -0
- package/build/tools/click-element.js +118 -0
- package/build/tools/get-console-logs.d.ts +7 -0
- package/build/tools/get-console-logs.js +55 -0
- package/build/tools/get-element-state.d.ts +14 -0
- package/build/tools/get-element-state.js +116 -0
- package/build/tools/get-errors.d.ts +7 -0
- package/build/tools/get-errors.js +40 -0
- package/build/tools/get-network-logs.d.ts +7 -0
- package/build/tools/get-network-logs.js +58 -0
- package/build/tools/get-process-output.d.ts +7 -0
- package/build/tools/get-process-output.js +55 -0
- package/build/tools/get-screenshot.d.ts +7 -0
- package/build/tools/get-screenshot.js +32 -0
- package/build/tools/index.d.ts +9 -0
- package/build/tools/index.js +117 -0
- package/build/tools/launch-electron.d.ts +13 -0
- package/build/tools/launch-electron.js +97 -0
- package/build/tools/launch-web-server.d.ts +13 -0
- package/build/tools/launch-web-server.js +88 -0
- package/build/tools/launch-windows-exe.d.ts +13 -0
- package/build/tools/launch-windows-exe.js +81 -0
- package/build/tools/navigate.d.ts +13 -0
- package/build/tools/navigate.js +137 -0
- package/build/tools/run-workflow.d.ts +14 -0
- package/build/tools/run-workflow.js +207 -0
- package/build/tools/screenshot-desktop.d.ts +13 -0
- package/build/tools/screenshot-desktop.js +80 -0
- package/build/tools/screenshot-electron.d.ts +13 -0
- package/build/tools/screenshot-electron.js +72 -0
- package/build/tools/screenshot-web.d.ts +13 -0
- package/build/tools/screenshot-web.js +129 -0
- package/build/tools/stop-process.d.ts +14 -0
- package/build/tools/stop-process.js +41 -0
- package/build/tools/type-text.d.ts +13 -0
- package/build/tools/type-text.js +137 -0
- package/build/tools/wait-for-element.d.ts +14 -0
- package/build/tools/wait-for-element.js +93 -0
- package/build/types/index.d.ts +31 -0
- package/build/types/index.js +4 -0
- package/build/utils/errors.d.ts +26 -0
- package/build/utils/errors.js +62 -0
- package/build/utils/shutdown.d.ts +16 -0
- package/build/utils/shutdown.js +34 -0
- package/build/workflow/assertions.d.ts +25 -0
- package/build/workflow/assertions.js +326 -0
- package/build/workflow/executor.d.ts +34 -0
- package/build/workflow/executor.js +269 -0
- package/build/workflow/types.d.ts +95 -0
- package/build/workflow/types.js +6 -0
- package/package.json +36 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow step executor
|
|
3
|
+
* Sequentially runs actions against a Playwright page with per-step
|
|
4
|
+
* screenshot capture and console/error log delta tracking.
|
|
5
|
+
*
|
|
6
|
+
* Stop-on-error semantics: failed steps still capture screenshot and
|
|
7
|
+
* log deltas for debugging context, then execution halts.
|
|
8
|
+
*/
|
|
9
|
+
import { resolveSelector } from "../interaction/selectors.js";
|
|
10
|
+
import { capturePlaywrightPage } from "../screenshot/capture.js";
|
|
11
|
+
import { optimizeScreenshot } from "../screenshot/optimize.js";
|
|
12
|
+
import { evaluateAssertion } from "./assertions.js";
|
|
13
|
+
/**
|
|
14
|
+
* Validate a workflow step has all required fields for its action type.
|
|
15
|
+
*
|
|
16
|
+
* @param step - The workflow step to validate
|
|
17
|
+
* @param index - Zero-based step index (for error messages)
|
|
18
|
+
* @returns null if valid, or a descriptive error string
|
|
19
|
+
*/
|
|
20
|
+
export function validateStep(step, index) {
|
|
21
|
+
switch (step.action) {
|
|
22
|
+
case "click":
|
|
23
|
+
if (!step.selector) {
|
|
24
|
+
return `Step ${index}: 'click' requires a 'selector' field`;
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
case "type":
|
|
28
|
+
if (!step.selector) {
|
|
29
|
+
return `Step ${index}: 'type' requires a 'selector' field`;
|
|
30
|
+
}
|
|
31
|
+
if (step.text === undefined || step.text === null) {
|
|
32
|
+
return `Step ${index}: 'type' requires a 'text' field`;
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
case "navigate":
|
|
36
|
+
if (!step.url) {
|
|
37
|
+
return `Step ${index}: 'navigate' requires a 'url' field`;
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
case "wait":
|
|
41
|
+
if (!step.selector) {
|
|
42
|
+
return `Step ${index}: 'wait' requires a 'selector' field`;
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
case "screenshot":
|
|
46
|
+
// No required fields
|
|
47
|
+
break;
|
|
48
|
+
case "assert": {
|
|
49
|
+
if (!step.selector) {
|
|
50
|
+
return `Step ${index}: 'assert' requires a 'selector' field`;
|
|
51
|
+
}
|
|
52
|
+
if (!step.assertType) {
|
|
53
|
+
return `Step ${index}: 'assert' requires an 'assertType' field`;
|
|
54
|
+
}
|
|
55
|
+
const needsExpected = ["text-equals", "text-contains", "value-equals", "attribute-equals"];
|
|
56
|
+
if (needsExpected.includes(step.assertType) && step.expected === undefined) {
|
|
57
|
+
return `Step ${index}: '${step.assertType}' assertion requires an 'expected' field`;
|
|
58
|
+
}
|
|
59
|
+
const needsAttribute = ["has-attribute", "attribute-equals"];
|
|
60
|
+
if (needsAttribute.includes(step.assertType) && !step.attribute) {
|
|
61
|
+
return `Step ${index}: '${step.assertType}' assertion requires an 'attribute' field`;
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
default:
|
|
66
|
+
return `Step ${index}: unknown action '${step.action}'`;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Execute a workflow — sequential steps against a Playwright page.
|
|
72
|
+
*
|
|
73
|
+
* Each step: validate -> execute action -> capture screenshot -> capture log deltas.
|
|
74
|
+
* On error: capture screenshot + log deltas for debugging, then stop.
|
|
75
|
+
*
|
|
76
|
+
* @returns WorkflowResult with per-step results, counts, and optional failedStep index
|
|
77
|
+
*/
|
|
78
|
+
export async function executeWorkflow(params) {
|
|
79
|
+
const { page, steps, sessionManager, sessionId } = params;
|
|
80
|
+
let pageIdentifier = params.pageIdentifier;
|
|
81
|
+
const results = [];
|
|
82
|
+
let lastConsoleCount = 0;
|
|
83
|
+
let lastErrorCount = 0;
|
|
84
|
+
for (let i = 0; i < steps.length; i++) {
|
|
85
|
+
const step = steps[i];
|
|
86
|
+
const result = {
|
|
87
|
+
stepIndex: i,
|
|
88
|
+
action: step.action,
|
|
89
|
+
success: false,
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
consoleDelta: [],
|
|
92
|
+
errorDelta: [],
|
|
93
|
+
};
|
|
94
|
+
// Validate step before execution
|
|
95
|
+
const validationError = validateStep(step, i);
|
|
96
|
+
if (validationError) {
|
|
97
|
+
result.error = validationError;
|
|
98
|
+
results.push(result);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
// Execute action via switch dispatch
|
|
103
|
+
switch (step.action) {
|
|
104
|
+
case "click": {
|
|
105
|
+
const locator = resolveSelector(page, step.selector);
|
|
106
|
+
await locator.click({
|
|
107
|
+
button: step.button ?? undefined,
|
|
108
|
+
clickCount: step.clickCount ?? undefined,
|
|
109
|
+
timeout: step.timeout ?? 30000,
|
|
110
|
+
});
|
|
111
|
+
// Post-click stability wait (matches click-element.ts pattern)
|
|
112
|
+
await Promise.race([
|
|
113
|
+
page.waitForLoadState("load").catch(() => { }),
|
|
114
|
+
new Promise((resolve) => setTimeout(resolve, 2000)),
|
|
115
|
+
]);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case "type": {
|
|
119
|
+
const locator = resolveSelector(page, step.selector);
|
|
120
|
+
const effectiveTimeout = step.timeout ?? 30000;
|
|
121
|
+
if (step.pressSequentially) {
|
|
122
|
+
// Clear first, then type char-by-char
|
|
123
|
+
await locator.fill("", { timeout: effectiveTimeout });
|
|
124
|
+
await locator.pressSequentially(step.text, {
|
|
125
|
+
delay: 50,
|
|
126
|
+
timeout: effectiveTimeout,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else if (step.clear === false) {
|
|
130
|
+
// Append mode: click to focus, then insertText
|
|
131
|
+
await locator.click({ timeout: effectiveTimeout });
|
|
132
|
+
await page.keyboard.insertText(step.text);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Default: fill() clears and types in one step
|
|
136
|
+
await locator.fill(step.text, { timeout: effectiveTimeout });
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case "navigate": {
|
|
141
|
+
await page.goto(step.url, {
|
|
142
|
+
waitUntil: "load",
|
|
143
|
+
timeout: step.timeout ?? 30000,
|
|
144
|
+
});
|
|
145
|
+
// Update PageReference URL (matches navigate.ts lines 120-128)
|
|
146
|
+
const oldRef = sessionManager.getPageRef(sessionId, pageIdentifier);
|
|
147
|
+
if (oldRef) {
|
|
148
|
+
sessionManager.removePageRef(sessionId, pageIdentifier);
|
|
149
|
+
sessionManager.setPageRef(sessionId, step.url, {
|
|
150
|
+
...oldRef,
|
|
151
|
+
url: step.url,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// Update local identifier for subsequent steps
|
|
155
|
+
pageIdentifier = step.url;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "screenshot": {
|
|
159
|
+
// No-op: screenshot is captured in the post-step phase below
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case "wait": {
|
|
163
|
+
const locator = resolveSelector(page, step.selector);
|
|
164
|
+
await locator.waitFor({
|
|
165
|
+
state: step.state ?? "visible",
|
|
166
|
+
timeout: step.timeout ?? 30000,
|
|
167
|
+
});
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case "assert": {
|
|
171
|
+
const effectiveTimeout = step.timeout ?? 30000;
|
|
172
|
+
const assertionResult = await evaluateAssertion(page, step, effectiveTimeout);
|
|
173
|
+
result.assertion = assertionResult;
|
|
174
|
+
if (!assertionResult.passed) {
|
|
175
|
+
// Failed assertion: capture screenshot + log deltas, then stop workflow
|
|
176
|
+
const rawBuffer = await capturePlaywrightPage(page, { fullPage: step.fullPage ?? false });
|
|
177
|
+
const optimized = await optimizeScreenshot(rawBuffer, { maxWidth: 1024, quality: 60 });
|
|
178
|
+
result.screenshotBase64 = optimized.data.toString("base64");
|
|
179
|
+
result.screenshotMimeType = optimized.mimeType;
|
|
180
|
+
// Capture log deltas
|
|
181
|
+
const allConsole = sessionManager.getConsoleCollectors(sessionId).flatMap((c) => [...c.getEntries()]);
|
|
182
|
+
const allErrors = sessionManager.getErrorCollectors(sessionId).flatMap((c) => [...c.getEntries()]);
|
|
183
|
+
result.consoleDelta = allConsole.slice(lastConsoleCount);
|
|
184
|
+
result.errorDelta = allErrors.slice(lastErrorCount);
|
|
185
|
+
lastConsoleCount = allConsole.length;
|
|
186
|
+
lastErrorCount = allErrors.length;
|
|
187
|
+
results.push(result);
|
|
188
|
+
break; // Break out of switch
|
|
189
|
+
}
|
|
190
|
+
break; // Switch break for passed assertions (falls through to normal screenshot/log capture)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Stop-on-error for failed assertions
|
|
194
|
+
if (step.action === "assert" && result.assertion && !result.assertion.passed) {
|
|
195
|
+
break; // Break the for loop
|
|
196
|
+
}
|
|
197
|
+
// Capture screenshot (aggressive optimization for workflows)
|
|
198
|
+
const rawBuffer = await capturePlaywrightPage(page, {
|
|
199
|
+
fullPage: step.fullPage ?? false,
|
|
200
|
+
});
|
|
201
|
+
const optimized = await optimizeScreenshot(rawBuffer, {
|
|
202
|
+
maxWidth: 1024,
|
|
203
|
+
quality: 60,
|
|
204
|
+
});
|
|
205
|
+
result.screenshotBase64 = optimized.data.toString("base64");
|
|
206
|
+
result.screenshotMimeType = optimized.mimeType;
|
|
207
|
+
// Capture log deltas (spread copy for mutation safety)
|
|
208
|
+
const allConsole = sessionManager
|
|
209
|
+
.getConsoleCollectors(sessionId)
|
|
210
|
+
.flatMap((c) => [...c.getEntries()]);
|
|
211
|
+
const allErrors = sessionManager
|
|
212
|
+
.getErrorCollectors(sessionId)
|
|
213
|
+
.flatMap((c) => [...c.getEntries()]);
|
|
214
|
+
result.consoleDelta = allConsole.slice(lastConsoleCount);
|
|
215
|
+
result.errorDelta = allErrors.slice(lastErrorCount);
|
|
216
|
+
lastConsoleCount = allConsole.length;
|
|
217
|
+
lastErrorCount = allErrors.length;
|
|
218
|
+
result.success = true;
|
|
219
|
+
results.push(result);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
// Set error message
|
|
223
|
+
result.error =
|
|
224
|
+
error instanceof Error ? error.message : String(error);
|
|
225
|
+
// Best-effort screenshot capture on failure
|
|
226
|
+
try {
|
|
227
|
+
const rawBuffer = await capturePlaywrightPage(page, {
|
|
228
|
+
fullPage: step.fullPage ?? false,
|
|
229
|
+
});
|
|
230
|
+
const optimized = await optimizeScreenshot(rawBuffer, {
|
|
231
|
+
maxWidth: 1024,
|
|
232
|
+
quality: 60,
|
|
233
|
+
});
|
|
234
|
+
result.screenshotBase64 = optimized.data.toString("base64");
|
|
235
|
+
result.screenshotMimeType = optimized.mimeType;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Silently skip if screenshot capture fails
|
|
239
|
+
}
|
|
240
|
+
// Still capture log deltas for debugging context
|
|
241
|
+
try {
|
|
242
|
+
const allConsole = sessionManager
|
|
243
|
+
.getConsoleCollectors(sessionId)
|
|
244
|
+
.flatMap((c) => [...c.getEntries()]);
|
|
245
|
+
const allErrors = sessionManager
|
|
246
|
+
.getErrorCollectors(sessionId)
|
|
247
|
+
.flatMap((c) => [...c.getEntries()]);
|
|
248
|
+
result.consoleDelta = allConsole.slice(lastConsoleCount);
|
|
249
|
+
result.errorDelta = allErrors.slice(lastErrorCount);
|
|
250
|
+
lastConsoleCount = allConsole.length;
|
|
251
|
+
lastErrorCount = allErrors.length;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Silently skip if log capture fails
|
|
255
|
+
}
|
|
256
|
+
results.push(result);
|
|
257
|
+
break; // Stop-on-error: halt after first failure
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Build workflow result
|
|
261
|
+
const completedSteps = results.filter((r) => r.success).length;
|
|
262
|
+
const failedResult = results.find((r) => !r.success);
|
|
263
|
+
return {
|
|
264
|
+
steps: results,
|
|
265
|
+
totalSteps: steps.length,
|
|
266
|
+
completedSteps,
|
|
267
|
+
failedStep: failedResult ? failedResult.stepIndex : undefined,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow execution types
|
|
3
|
+
* Defines step schemas, per-step results, and overall workflow results
|
|
4
|
+
* for the sequential action executor (QA-01/QA-02).
|
|
5
|
+
*/
|
|
6
|
+
import type { ConsoleEntry, ErrorEntry } from "../capture/types.js";
|
|
7
|
+
/**
|
|
8
|
+
* A single workflow step — flat schema with action-specific optional fields.
|
|
9
|
+
*
|
|
10
|
+
* Required fields per action:
|
|
11
|
+
* - click: selector
|
|
12
|
+
* - type: selector, text (text can be empty string but must be defined)
|
|
13
|
+
* - navigate: url
|
|
14
|
+
* - wait: selector
|
|
15
|
+
* - screenshot: (no required fields)
|
|
16
|
+
* - assert: selector, assertType (plus expected/attribute depending on assertType)
|
|
17
|
+
*/
|
|
18
|
+
export interface WorkflowStep {
|
|
19
|
+
/** Action to perform */
|
|
20
|
+
action: "click" | "type" | "navigate" | "screenshot" | "wait" | "assert";
|
|
21
|
+
/** Element selector — required for click, type, wait */
|
|
22
|
+
selector?: string;
|
|
23
|
+
/** Text to type — required for type action */
|
|
24
|
+
text?: string;
|
|
25
|
+
/** URL to navigate to — required for navigate action */
|
|
26
|
+
url?: string;
|
|
27
|
+
/** Mouse button for click (default: left) */
|
|
28
|
+
button?: "left" | "right" | "middle";
|
|
29
|
+
/** Number of clicks for click action (1-3, e.g. 2 for double-click) */
|
|
30
|
+
clickCount?: number;
|
|
31
|
+
/** Type one character at a time instead of fill/paste (default: false) */
|
|
32
|
+
pressSequentially?: boolean;
|
|
33
|
+
/** Clear field before typing (default: true). Set false to append. */
|
|
34
|
+
clear?: boolean;
|
|
35
|
+
/** Capture full scrollable page for screenshot step */
|
|
36
|
+
fullPage?: boolean;
|
|
37
|
+
/** Wait state for wait action (default: visible) */
|
|
38
|
+
state?: "visible" | "hidden" | "attached" | "detached";
|
|
39
|
+
/** Assertion type — required for assert action */
|
|
40
|
+
assertType?: "exists" | "not-exists" | "visible" | "hidden" | "text-equals" | "text-contains" | "has-attribute" | "attribute-equals" | "enabled" | "disabled" | "checked" | "not-checked" | "value-equals";
|
|
41
|
+
/** Expected value for text/attribute assertions */
|
|
42
|
+
expected?: string;
|
|
43
|
+
/** Attribute name for attribute assertions */
|
|
44
|
+
attribute?: string;
|
|
45
|
+
/** Per-step timeout in ms (default: 30000) */
|
|
46
|
+
timeout?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Result of a single workflow step execution.
|
|
50
|
+
* Always includes log deltas for debugging context.
|
|
51
|
+
* Screenshot is captured even on failure (best-effort).
|
|
52
|
+
*/
|
|
53
|
+
export interface StepResult {
|
|
54
|
+
/** Zero-based index of this step in the workflow */
|
|
55
|
+
stepIndex: number;
|
|
56
|
+
/** Action that was executed */
|
|
57
|
+
action: string;
|
|
58
|
+
/** Whether the step completed successfully */
|
|
59
|
+
success: boolean;
|
|
60
|
+
/** ISO timestamp when step execution started */
|
|
61
|
+
timestamp: string;
|
|
62
|
+
/** Base64-encoded screenshot captured after step (WebP, quality 60, max 1024px wide) */
|
|
63
|
+
screenshotBase64?: string;
|
|
64
|
+
/** MIME type of the screenshot (image/webp) */
|
|
65
|
+
screenshotMimeType?: string;
|
|
66
|
+
/** Console log entries captured during this step */
|
|
67
|
+
consoleDelta: ConsoleEntry[];
|
|
68
|
+
/** Error entries captured during this step */
|
|
69
|
+
errorDelta: ErrorEntry[];
|
|
70
|
+
/** Error message if step failed */
|
|
71
|
+
error?: string;
|
|
72
|
+
/** Structured assertion result — only present for assert steps */
|
|
73
|
+
assertion?: {
|
|
74
|
+
passed: boolean;
|
|
75
|
+
assertType: string;
|
|
76
|
+
selector: string;
|
|
77
|
+
expected: string | null;
|
|
78
|
+
actual: string | null;
|
|
79
|
+
message: string;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Overall workflow execution result.
|
|
84
|
+
* Contains all step results and summary counts.
|
|
85
|
+
*/
|
|
86
|
+
export interface WorkflowResult {
|
|
87
|
+
/** Results for each executed step (may be fewer than totalSteps on failure) */
|
|
88
|
+
steps: StepResult[];
|
|
89
|
+
/** Total number of steps in the input workflow */
|
|
90
|
+
totalSteps: number;
|
|
91
|
+
/** Number of steps that completed successfully */
|
|
92
|
+
completedSteps: number;
|
|
93
|
+
/** Index of the failed step, if any */
|
|
94
|
+
failedStep?: number;
|
|
95
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auto-feedback",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "MCP server for GUI testing and feedback - gives Claude Code eyes and hands",
|
|
6
|
+
"bin": {
|
|
7
|
+
"auto-feedback": "./build/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"build"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsx src/index.ts",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
19
|
+
"detect-port": "^2.1.0",
|
|
20
|
+
"node-screenshots": "^0.2.1",
|
|
21
|
+
"playwright": "^1.58.2",
|
|
22
|
+
"sharp": "^0.34.5",
|
|
23
|
+
"tree-kill": "^1.2.2",
|
|
24
|
+
"wait-on": "^9.0.3",
|
|
25
|
+
"zod": "^3.25.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"@types/wait-on": "^5.3.4",
|
|
30
|
+
"tsx": "^4.0.0",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|