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.
Files changed (90) hide show
  1. package/README.md +180 -0
  2. package/build/capture/console-collector.d.ts +16 -0
  3. package/build/capture/console-collector.js +43 -0
  4. package/build/capture/error-collector.d.ts +15 -0
  5. package/build/capture/error-collector.js +47 -0
  6. package/build/capture/network-collector.d.ts +16 -0
  7. package/build/capture/network-collector.js +76 -0
  8. package/build/capture/process-collector.d.ts +16 -0
  9. package/build/capture/process-collector.js +48 -0
  10. package/build/capture/types.d.ts +61 -0
  11. package/build/capture/types.js +5 -0
  12. package/build/index.d.ts +6 -0
  13. package/build/index.js +41 -0
  14. package/build/interaction/selectors.d.ts +26 -0
  15. package/build/interaction/selectors.js +84 -0
  16. package/build/interaction/types.d.ts +56 -0
  17. package/build/interaction/types.js +5 -0
  18. package/build/process/cleanup.d.ts +23 -0
  19. package/build/process/cleanup.js +50 -0
  20. package/build/process/launcher.d.ts +22 -0
  21. package/build/process/launcher.js +54 -0
  22. package/build/process/monitor.d.ts +14 -0
  23. package/build/process/monitor.js +67 -0
  24. package/build/process/types.d.ts +84 -0
  25. package/build/process/types.js +5 -0
  26. package/build/screenshot/auto-capture.d.ts +14 -0
  27. package/build/screenshot/auto-capture.js +38 -0
  28. package/build/screenshot/capture.d.ts +21 -0
  29. package/build/screenshot/capture.js +48 -0
  30. package/build/screenshot/optimize.d.ts +19 -0
  31. package/build/screenshot/optimize.js +28 -0
  32. package/build/screenshot/types.d.ts +43 -0
  33. package/build/screenshot/types.js +4 -0
  34. package/build/server.d.ts +10 -0
  35. package/build/server.js +18 -0
  36. package/build/session-manager.d.ts +119 -0
  37. package/build/session-manager.js +284 -0
  38. package/build/tools/check-port.d.ts +10 -0
  39. package/build/tools/check-port.js +40 -0
  40. package/build/tools/click-element.d.ts +13 -0
  41. package/build/tools/click-element.js +118 -0
  42. package/build/tools/get-console-logs.d.ts +7 -0
  43. package/build/tools/get-console-logs.js +55 -0
  44. package/build/tools/get-element-state.d.ts +14 -0
  45. package/build/tools/get-element-state.js +116 -0
  46. package/build/tools/get-errors.d.ts +7 -0
  47. package/build/tools/get-errors.js +40 -0
  48. package/build/tools/get-network-logs.d.ts +7 -0
  49. package/build/tools/get-network-logs.js +58 -0
  50. package/build/tools/get-process-output.d.ts +7 -0
  51. package/build/tools/get-process-output.js +55 -0
  52. package/build/tools/get-screenshot.d.ts +7 -0
  53. package/build/tools/get-screenshot.js +32 -0
  54. package/build/tools/index.d.ts +9 -0
  55. package/build/tools/index.js +117 -0
  56. package/build/tools/launch-electron.d.ts +13 -0
  57. package/build/tools/launch-electron.js +97 -0
  58. package/build/tools/launch-web-server.d.ts +13 -0
  59. package/build/tools/launch-web-server.js +88 -0
  60. package/build/tools/launch-windows-exe.d.ts +13 -0
  61. package/build/tools/launch-windows-exe.js +81 -0
  62. package/build/tools/navigate.d.ts +13 -0
  63. package/build/tools/navigate.js +137 -0
  64. package/build/tools/run-workflow.d.ts +14 -0
  65. package/build/tools/run-workflow.js +207 -0
  66. package/build/tools/screenshot-desktop.d.ts +13 -0
  67. package/build/tools/screenshot-desktop.js +80 -0
  68. package/build/tools/screenshot-electron.d.ts +13 -0
  69. package/build/tools/screenshot-electron.js +72 -0
  70. package/build/tools/screenshot-web.d.ts +13 -0
  71. package/build/tools/screenshot-web.js +129 -0
  72. package/build/tools/stop-process.d.ts +14 -0
  73. package/build/tools/stop-process.js +41 -0
  74. package/build/tools/type-text.d.ts +13 -0
  75. package/build/tools/type-text.js +137 -0
  76. package/build/tools/wait-for-element.d.ts +14 -0
  77. package/build/tools/wait-for-element.js +93 -0
  78. package/build/types/index.d.ts +31 -0
  79. package/build/types/index.js +4 -0
  80. package/build/utils/errors.d.ts +26 -0
  81. package/build/utils/errors.js +62 -0
  82. package/build/utils/shutdown.d.ts +16 -0
  83. package/build/utils/shutdown.js +34 -0
  84. package/build/workflow/assertions.d.ts +25 -0
  85. package/build/workflow/assertions.js +326 -0
  86. package/build/workflow/executor.d.ts +34 -0
  87. package/build/workflow/executor.js +269 -0
  88. package/build/workflow/types.d.ts +95 -0
  89. package/build/workflow/types.js +6 -0
  90. package/package.json +36 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Structured error formatting for tool results
3
+ */
4
+ /**
5
+ * Create a tool error response with structured information
6
+ * @param message Error message
7
+ * @param context Optional context about what was being attempted
8
+ * @param suggestedFix Optional suggestion for how to fix the issue
9
+ * @returns ToolResult with isError: true
10
+ */
11
+ export function createToolError(message, context, suggestedFix) {
12
+ let text = `Error: ${message}`;
13
+ if (context) {
14
+ text += `\nContext: ${context}`;
15
+ }
16
+ if (suggestedFix) {
17
+ text += `\nSuggested fix: ${suggestedFix}`;
18
+ }
19
+ return {
20
+ content: [{ type: "text", text }],
21
+ isError: true,
22
+ };
23
+ }
24
+ /**
25
+ * Create a successful tool result
26
+ * @param data Data to serialize as JSON
27
+ * @returns ToolResult with isError: false
28
+ */
29
+ export function createToolResult(data) {
30
+ return {
31
+ content: [
32
+ {
33
+ type: "text",
34
+ text: JSON.stringify(data, null, 2),
35
+ },
36
+ ],
37
+ isError: false,
38
+ };
39
+ }
40
+ /**
41
+ * Create a screenshot tool result with text metadata and image content
42
+ * @param metadata Metadata to include as JSON text
43
+ * @param imageBase64 Base64-encoded image data
44
+ * @param mimeType Image MIME type (default: 'image/webp')
45
+ * @returns ToolResult with text + image content
46
+ */
47
+ export function createScreenshotResult(metadata, imageBase64, mimeType = "image/webp") {
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: JSON.stringify(metadata, null, 2),
53
+ },
54
+ {
55
+ type: "image",
56
+ data: imageBase64,
57
+ mimeType,
58
+ },
59
+ ],
60
+ isError: false,
61
+ };
62
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Graceful shutdown coordinator with cleanup handler registry
3
+ */
4
+ export declare class ShutdownManager {
5
+ private cleanupHandlers;
6
+ private isShuttingDown;
7
+ /**
8
+ * Register a cleanup handler to run during shutdown
9
+ */
10
+ register(handler: () => Promise<void>): void;
11
+ /**
12
+ * Execute all cleanup handlers and exit process
13
+ * Safe to call multiple times - subsequent calls are ignored
14
+ */
15
+ cleanup(): Promise<void>;
16
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Graceful shutdown coordinator with cleanup handler registry
3
+ */
4
+ export class ShutdownManager {
5
+ cleanupHandlers = [];
6
+ isShuttingDown = false;
7
+ /**
8
+ * Register a cleanup handler to run during shutdown
9
+ */
10
+ register(handler) {
11
+ this.cleanupHandlers.push(handler);
12
+ }
13
+ /**
14
+ * Execute all cleanup handlers and exit process
15
+ * Safe to call multiple times - subsequent calls are ignored
16
+ */
17
+ async cleanup() {
18
+ if (this.isShuttingDown) {
19
+ return;
20
+ }
21
+ this.isShuttingDown = true;
22
+ console.error("Shutting down...");
23
+ // Run all handlers in parallel with individual try/catch
24
+ const results = await Promise.allSettled(this.cleanupHandlers.map((handler) => handler()));
25
+ // Log any handler failures
26
+ results.forEach((result, index) => {
27
+ if (result.status === "rejected") {
28
+ console.error(`Cleanup handler ${index} failed:`, result.reason);
29
+ }
30
+ });
31
+ console.error("Shutdown complete");
32
+ process.exit(0);
33
+ }
34
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Assertion evaluation for workflow assert steps.
3
+ * Queries element state via Playwright locator methods and returns
4
+ * structured pass/fail results.
5
+ */
6
+ import type { Page } from "playwright";
7
+ import type { WorkflowStep } from "./types.js";
8
+ /**
9
+ * Structured result of an assertion evaluation.
10
+ */
11
+ export interface AssertionResult {
12
+ passed: boolean;
13
+ assertType: string;
14
+ selector: string;
15
+ expected: string | null;
16
+ actual: string | null;
17
+ message: string;
18
+ }
19
+ /**
20
+ * Evaluate an assertion step against a Playwright page.
21
+ *
22
+ * Assertion failures produce { passed: false } results, NOT thrown exceptions.
23
+ * Only unexpected errors (page crash, browser disconnect) propagate as throws.
24
+ */
25
+ export declare function evaluateAssertion(page: Page, step: WorkflowStep, timeout: number): Promise<AssertionResult>;
@@ -0,0 +1,326 @@
1
+ /**
2
+ * Assertion evaluation for workflow assert steps.
3
+ * Queries element state via Playwright locator methods and returns
4
+ * structured pass/fail results.
5
+ */
6
+ import { resolveSelector } from "../interaction/selectors.js";
7
+ /**
8
+ * Evaluate an assertion step against a Playwright page.
9
+ *
10
+ * Assertion failures produce { passed: false } results, NOT thrown exceptions.
11
+ * Only unexpected errors (page crash, browser disconnect) propagate as throws.
12
+ */
13
+ export async function evaluateAssertion(page, step, timeout) {
14
+ const base = {
15
+ assertType: step.assertType,
16
+ selector: step.selector,
17
+ };
18
+ const locator = resolveSelector(page, step.selector);
19
+ switch (step.assertType) {
20
+ case "exists": {
21
+ const count = await locator.count();
22
+ const passed = count > 0;
23
+ return {
24
+ ...base,
25
+ passed,
26
+ expected: "element exists in DOM",
27
+ actual: count > 0 ? `found (${count} match${count > 1 ? "es" : ""})` : "not found",
28
+ message: passed
29
+ ? `PASS: Element "${step.selector}" exists (${count} match${count > 1 ? "es" : ""})`
30
+ : `FAIL: Element "${step.selector}" does not exist`,
31
+ };
32
+ }
33
+ case "not-exists": {
34
+ const count = await locator.count();
35
+ const passed = count === 0;
36
+ return {
37
+ ...base,
38
+ passed,
39
+ expected: "element does not exist in DOM",
40
+ actual: count === 0 ? "not found" : `found (${count} match${count > 1 ? "es" : ""})`,
41
+ message: passed
42
+ ? `PASS: Element "${step.selector}" does not exist`
43
+ : `FAIL: Element "${step.selector}" exists (${count} match${count > 1 ? "es" : ""})`,
44
+ };
45
+ }
46
+ case "visible": {
47
+ try {
48
+ await locator.waitFor({ state: "attached", timeout });
49
+ }
50
+ catch {
51
+ return {
52
+ ...base,
53
+ passed: false,
54
+ expected: "element is visible",
55
+ actual: "element not found in DOM",
56
+ message: `FAIL: Element "${step.selector}" not found in DOM (timeout waiting for attachment)`,
57
+ };
58
+ }
59
+ const visible = await locator.isVisible();
60
+ return {
61
+ ...base,
62
+ passed: visible,
63
+ expected: "element is visible",
64
+ actual: visible ? "visible" : "hidden",
65
+ message: visible
66
+ ? `PASS: Element "${step.selector}" is visible`
67
+ : `FAIL: Element "${step.selector}" is hidden`,
68
+ };
69
+ }
70
+ case "hidden": {
71
+ const count = await locator.count();
72
+ if (count === 0) {
73
+ return {
74
+ ...base,
75
+ passed: true,
76
+ expected: "element is hidden",
77
+ actual: "not in DOM (hidden)",
78
+ message: `PASS: Element "${step.selector}" is not in DOM (counts as hidden)`,
79
+ };
80
+ }
81
+ const visible = await locator.isVisible();
82
+ const passed = !visible;
83
+ return {
84
+ ...base,
85
+ passed,
86
+ expected: "element is hidden",
87
+ actual: visible ? "visible" : "hidden",
88
+ message: passed
89
+ ? `PASS: Element "${step.selector}" is hidden`
90
+ : `FAIL: Element "${step.selector}" is visible`,
91
+ };
92
+ }
93
+ case "text-equals": {
94
+ try {
95
+ await locator.waitFor({ state: "attached", timeout });
96
+ }
97
+ catch {
98
+ return {
99
+ ...base,
100
+ passed: false,
101
+ expected: `text equals "${step.expected}"`,
102
+ actual: "element not found in DOM",
103
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
104
+ };
105
+ }
106
+ const text = (await locator.innerText({ timeout })).trim();
107
+ const passed = text === step.expected;
108
+ return {
109
+ ...base,
110
+ passed,
111
+ expected: `text equals "${step.expected}"`,
112
+ actual: `"${text}"`,
113
+ message: passed
114
+ ? `PASS: Text of "${step.selector}" equals "${step.expected}"`
115
+ : `FAIL: Text of "${step.selector}" is "${text}", expected "${step.expected}"`,
116
+ };
117
+ }
118
+ case "text-contains": {
119
+ try {
120
+ await locator.waitFor({ state: "attached", timeout });
121
+ }
122
+ catch {
123
+ return {
124
+ ...base,
125
+ passed: false,
126
+ expected: `text contains "${step.expected}"`,
127
+ actual: "element not found in DOM",
128
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
129
+ };
130
+ }
131
+ const text = await locator.innerText({ timeout });
132
+ const passed = text.includes(step.expected ?? "");
133
+ return {
134
+ ...base,
135
+ passed,
136
+ expected: `text contains "${step.expected}"`,
137
+ actual: `"${text}"`,
138
+ message: passed
139
+ ? `PASS: Text of "${step.selector}" contains "${step.expected}"`
140
+ : `FAIL: Text of "${step.selector}" is "${text}", does not contain "${step.expected}"`,
141
+ };
142
+ }
143
+ case "has-attribute": {
144
+ try {
145
+ await locator.waitFor({ state: "attached", timeout });
146
+ }
147
+ catch {
148
+ return {
149
+ ...base,
150
+ passed: false,
151
+ expected: `has attribute "${step.attribute}"`,
152
+ actual: "element not found in DOM",
153
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
154
+ };
155
+ }
156
+ const value = await locator.getAttribute(step.attribute, { timeout });
157
+ const passed = value !== null;
158
+ return {
159
+ ...base,
160
+ passed,
161
+ expected: `has attribute "${step.attribute}"`,
162
+ actual: passed ? `attribute "${step.attribute}" present (value: "${value}")` : `attribute "${step.attribute}" not found`,
163
+ message: passed
164
+ ? `PASS: Element "${step.selector}" has attribute "${step.attribute}"`
165
+ : `FAIL: Element "${step.selector}" does not have attribute "${step.attribute}"`,
166
+ };
167
+ }
168
+ case "attribute-equals": {
169
+ try {
170
+ await locator.waitFor({ state: "attached", timeout });
171
+ }
172
+ catch {
173
+ return {
174
+ ...base,
175
+ passed: false,
176
+ expected: `attribute "${step.attribute}" equals "${step.expected}"`,
177
+ actual: "element not found in DOM",
178
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
179
+ };
180
+ }
181
+ const value = await locator.getAttribute(step.attribute, { timeout });
182
+ const passed = value === step.expected;
183
+ return {
184
+ ...base,
185
+ passed,
186
+ expected: `attribute "${step.attribute}" equals "${step.expected}"`,
187
+ actual: value !== null ? `"${value}"` : "attribute not found",
188
+ message: passed
189
+ ? `PASS: Attribute "${step.attribute}" of "${step.selector}" equals "${step.expected}"`
190
+ : `FAIL: Attribute "${step.attribute}" of "${step.selector}" is ${value !== null ? `"${value}"` : "not found"}, expected "${step.expected}"`,
191
+ };
192
+ }
193
+ case "enabled": {
194
+ try {
195
+ await locator.waitFor({ state: "attached", timeout });
196
+ }
197
+ catch {
198
+ return {
199
+ ...base,
200
+ passed: false,
201
+ expected: "element is enabled",
202
+ actual: "element not found in DOM",
203
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
204
+ };
205
+ }
206
+ const enabled = await locator.isEnabled({ timeout });
207
+ return {
208
+ ...base,
209
+ passed: enabled,
210
+ expected: "element is enabled",
211
+ actual: enabled ? "enabled" : "disabled",
212
+ message: enabled
213
+ ? `PASS: Element "${step.selector}" is enabled`
214
+ : `FAIL: Element "${step.selector}" is disabled`,
215
+ };
216
+ }
217
+ case "disabled": {
218
+ try {
219
+ await locator.waitFor({ state: "attached", timeout });
220
+ }
221
+ catch {
222
+ return {
223
+ ...base,
224
+ passed: false,
225
+ expected: "element is disabled",
226
+ actual: "element not found in DOM",
227
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
228
+ };
229
+ }
230
+ const enabled = await locator.isEnabled({ timeout });
231
+ const passed = !enabled;
232
+ return {
233
+ ...base,
234
+ passed,
235
+ expected: "element is disabled",
236
+ actual: enabled ? "enabled" : "disabled",
237
+ message: passed
238
+ ? `PASS: Element "${step.selector}" is disabled`
239
+ : `FAIL: Element "${step.selector}" is enabled`,
240
+ };
241
+ }
242
+ case "checked": {
243
+ try {
244
+ await locator.waitFor({ state: "attached", timeout });
245
+ }
246
+ catch {
247
+ return {
248
+ ...base,
249
+ passed: false,
250
+ expected: "element is checked",
251
+ actual: "element not found in DOM",
252
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
253
+ };
254
+ }
255
+ const checked = await locator.isChecked({ timeout });
256
+ return {
257
+ ...base,
258
+ passed: checked,
259
+ expected: "element is checked",
260
+ actual: checked ? "checked" : "not checked",
261
+ message: checked
262
+ ? `PASS: Element "${step.selector}" is checked`
263
+ : `FAIL: Element "${step.selector}" is not checked`,
264
+ };
265
+ }
266
+ case "not-checked": {
267
+ try {
268
+ await locator.waitFor({ state: "attached", timeout });
269
+ }
270
+ catch {
271
+ return {
272
+ ...base,
273
+ passed: false,
274
+ expected: "element is not checked",
275
+ actual: "element not found in DOM",
276
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
277
+ };
278
+ }
279
+ const checked = await locator.isChecked({ timeout });
280
+ const passed = !checked;
281
+ return {
282
+ ...base,
283
+ passed,
284
+ expected: "element is not checked",
285
+ actual: checked ? "checked" : "not checked",
286
+ message: passed
287
+ ? `PASS: Element "${step.selector}" is not checked`
288
+ : `FAIL: Element "${step.selector}" is checked`,
289
+ };
290
+ }
291
+ case "value-equals": {
292
+ try {
293
+ await locator.waitFor({ state: "attached", timeout });
294
+ }
295
+ catch {
296
+ return {
297
+ ...base,
298
+ passed: false,
299
+ expected: `value equals "${step.expected}"`,
300
+ actual: "element not found in DOM",
301
+ message: `FAIL: Element "${step.selector}" not found in DOM`,
302
+ };
303
+ }
304
+ const value = await locator.inputValue({ timeout });
305
+ const passed = value === step.expected;
306
+ return {
307
+ ...base,
308
+ passed,
309
+ expected: `value equals "${step.expected}"`,
310
+ actual: `"${value}"`,
311
+ message: passed
312
+ ? `PASS: Value of "${step.selector}" equals "${step.expected}"`
313
+ : `FAIL: Value of "${step.selector}" is "${value}", expected "${step.expected}"`,
314
+ };
315
+ }
316
+ default: {
317
+ return {
318
+ ...base,
319
+ passed: false,
320
+ expected: null,
321
+ actual: null,
322
+ message: `Unknown assertion type: ${step.assertType}`,
323
+ };
324
+ }
325
+ }
326
+ }
@@ -0,0 +1,34 @@
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 type { Page } from "playwright";
10
+ import type { SessionManager } from "../session-manager.js";
11
+ import type { WorkflowStep, WorkflowResult } from "./types.js";
12
+ /**
13
+ * Validate a workflow step has all required fields for its action type.
14
+ *
15
+ * @param step - The workflow step to validate
16
+ * @param index - Zero-based step index (for error messages)
17
+ * @returns null if valid, or a descriptive error string
18
+ */
19
+ export declare function validateStep(step: WorkflowStep, index: number): string | null;
20
+ /**
21
+ * Execute a workflow — sequential steps against a Playwright page.
22
+ *
23
+ * Each step: validate -> execute action -> capture screenshot -> capture log deltas.
24
+ * On error: capture screenshot + log deltas for debugging, then stop.
25
+ *
26
+ * @returns WorkflowResult with per-step results, counts, and optional failedStep index
27
+ */
28
+ export declare function executeWorkflow(params: {
29
+ page: Page;
30
+ steps: WorkflowStep[];
31
+ sessionManager: SessionManager;
32
+ sessionId: string;
33
+ pageIdentifier: string;
34
+ }): Promise<WorkflowResult>;