pi-harness-runtime 1.1.181 → 1.1.183

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 (114) hide show
  1. package/harness/e2e-commands.ts +216 -0
  2. package/harness/honcho-memory.ts +3 -3
  3. package/index.ts +9 -0
  4. package/package.json +1 -1
  5. package/packages/a2a-adapter/package.json +1 -1
  6. package/packages/agent-policy/package.json +1 -1
  7. package/packages/architecture-generator/package.json +1 -1
  8. package/packages/auth/package.json +1 -1
  9. package/packages/autonomous-refactor/package.json +1 -1
  10. package/packages/autonomous-runtime/package.json +1 -1
  11. package/packages/cache-strategy/package.json +1 -1
  12. package/packages/capability-registry/package.json +1 -1
  13. package/packages/checkpoint/package.json +1 -1
  14. package/packages/cli-plugin-sdk/package.json +1 -1
  15. package/packages/clipboard/package.json +1 -1
  16. package/packages/clipboard-plugin/package.json +1 -1
  17. package/packages/code-generation/package.json +1 -1
  18. package/packages/code-review/package.json +1 -1
  19. package/packages/codex-adapter/package.json +1 -1
  20. package/packages/codex-watcher/package.json +1 -1
  21. package/packages/config-capture/package.json +1 -1
  22. package/packages/context-compiler/package.json +1 -1
  23. package/packages/context-discovery/package.json +1 -1
  24. package/packages/context-manager/package.json +1 -1
  25. package/packages/cookie-sanitizer/package.json +1 -1
  26. package/packages/cost-optimizer/package.json +1 -1
  27. package/packages/dependency-analyzer/package.json +1 -1
  28. package/packages/django-plugin/package.json +1 -1
  29. package/packages/doc-generator/package.json +1 -1
  30. package/packages/e2e-tester/README.md +142 -0
  31. package/packages/e2e-tester/dist/index.d.ts +133 -0
  32. package/packages/e2e-tester/dist/index.js +380 -0
  33. package/packages/e2e-tester/dist/types.d.ts +91 -0
  34. package/packages/e2e-tester/dist/types.js +17 -0
  35. package/packages/e2e-tester/package.json +31 -0
  36. package/packages/e2e-tester/src/index.ts +488 -0
  37. package/packages/e2e-tester/src/types.ts +95 -0
  38. package/packages/evaluation-engine/package.json +1 -1
  39. package/packages/evaluation-runner/package.json +1 -1
  40. package/packages/event-bus/package.json +1 -1
  41. package/packages/event-store/package.json +1 -1
  42. package/packages/experience-replay/package.json +1 -1
  43. package/packages/feedback-collector/package.json +1 -1
  44. package/packages/file-copy-helper/package.json +1 -1
  45. package/packages/framework-detector/package.json +1 -1
  46. package/packages/framework-plugin-sdk/package.json +1 -1
  47. package/packages/frappe-plugin/package.json +1 -1
  48. package/packages/generic-web-plugin/package.json +1 -1
  49. package/packages/health-monitor/package.json +1 -1
  50. package/packages/intent-analyzer/package.json +1 -1
  51. package/packages/jev-judge/package.json +1 -1
  52. package/packages/knowledge-graph/package.json +1 -1
  53. package/packages/knowledge-retrieval/package.json +1 -1
  54. package/packages/laravel-plugin/package.json +1 -1
  55. package/packages/learning-engine/package.json +1 -1
  56. package/packages/mcp-adapter/package.json +1 -1
  57. package/packages/memory-engine/package.json +1 -1
  58. package/packages/milestone-manager/package.json +1 -1
  59. package/packages/model-registry/package.json +1 -1
  60. package/packages/nextjs-plugin/package.json +1 -1
  61. package/packages/notification/package.json +1 -1
  62. package/packages/observability/package.json +1 -1
  63. package/packages/okf-indexer/package.json +1 -1
  64. package/packages/paperclip-integration/package.json +1 -1
  65. package/packages/performance-optimizer/package.json +1 -1
  66. package/packages/privilege-broker/package.json +1 -1
  67. package/packages/project-analyzer/package.json +1 -1
  68. package/packages/project-bootstrap/package.json +1 -1
  69. package/packages/projection-engine/package.json +1 -1
  70. package/packages/prompt-compiler/package.json +1 -1
  71. package/packages/prompt-versioning/package.json +1 -1
  72. package/packages/provider-adapter-sdk/package.json +1 -1
  73. package/packages/provider-router/package.json +1 -1
  74. package/packages/provider-selector/package.json +1 -1
  75. package/packages/providers/package.json +1 -1
  76. package/packages/qdrant-skills/package.json +1 -1
  77. package/packages/quota-manager/package.json +1 -1
  78. package/packages/rate-limiter/package.json +1 -1
  79. package/packages/react-vite-plugin/package.json +1 -1
  80. package/packages/release-manager/package.json +1 -1
  81. package/packages/requirement-compiler/package.json +1 -1
  82. package/packages/runtime/package.json +1 -1
  83. package/packages/scheduler/package.json +1 -1
  84. package/packages/scheduler-adapter/package.json +1 -1
  85. package/packages/session/package.json +1 -1
  86. package/packages/session-api/package.json +1 -1
  87. package/packages/session-export/package.json +1 -1
  88. package/packages/shared-context/package.json +1 -1
  89. package/packages/skill-mcp-client/package.json +1 -1
  90. package/packages/skill-registry/package.json +1 -1
  91. package/packages/skills/package.json +1 -1
  92. package/packages/sprint-planner/package.json +1 -1
  93. package/packages/subscription-engine/package.json +1 -1
  94. package/packages/task-compiler/package.json +1 -1
  95. package/packages/telegram/package.json +1 -1
  96. package/packages/tencentdb-memory/package.json +1 -1
  97. package/packages/tencentdb-sync/package.json +1 -1
  98. package/packages/test-data-generator/package.json +1 -1
  99. package/packages/test-generator/package.json +1 -1
  100. package/packages/todo-bd-sync/package.json +1 -1
  101. package/packages/token-estimation/package.json +1 -1
  102. package/packages/token-optimizer/package.json +1 -1
  103. package/packages/trajectory/package.json +1 -1
  104. package/packages/tui/package.json +1 -1
  105. package/packages/types/package.json +1 -1
  106. package/packages/webmcp-tester/dist/index.d.ts +178 -0
  107. package/packages/webmcp-tester/dist/index.js +412 -0
  108. package/packages/webmcp-tester/package.json +27 -0
  109. package/packages/webmcp-tester/src/index.ts +571 -0
  110. package/packages/workflow-events/package.json +1 -1
  111. package/packages/workspace-scanner/package.json +1 -1
  112. package/packages/worktree/package.json +1 -1
  113. package/packages/write-review/package.json +1 -1
  114. package/scripts/run-e2e-test.ts +214 -0
@@ -0,0 +1,488 @@
1
+ /**
2
+ * WebMCP E2E Tester
3
+ *
4
+ * Natural language E2E testing with WebMCP protocol.
5
+ * Works with: https://github.com/GoogleChromeLabs/webmcp-extension/
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * import { WebMCPTester } from "@pi-harness/e2e-tester";
10
+ *
11
+ * const tester = new WebMCPTester();
12
+ * const result = await tester.run({
13
+ * name: "Registration Flow",
14
+ * url: "https://amos-saas.example.com/register",
15
+ * steps: [
16
+ * { action: "fill", field: "email", value: "test@example.com" },
17
+ * { action: "click", target: "Submit" },
18
+ * { action: "assert", expected: "Welcome" },
19
+ * ],
20
+ * });
21
+ * ```
22
+ */
23
+
24
+ import { homedir } from "os";
25
+ import { join } from "path";
26
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
27
+
28
+ export * from "./types.js";
29
+
30
+ const DEFAULT_CONFIG = {
31
+ port: 9222,
32
+ headless: true,
33
+ timeoutMs: 60000,
34
+ screenshotDir: join(homedir(), ".pi-harness-runtime", "test-screenshots"),
35
+ };
36
+
37
+ export interface TestStep {
38
+ action: "navigate" | "fill" | "click" | "submit" | "assert" | "wait" | "screenshot" | "select";
39
+ target?: string;
40
+ field?: string;
41
+ value?: string;
42
+ expected?: string;
43
+ waitMs?: number;
44
+ screenshot?: boolean;
45
+ }
46
+
47
+ export interface TestScenario {
48
+ name: string;
49
+ url: string;
50
+ auth?: {
51
+ type: "form" | "cookie" | "token";
52
+ username?: string;
53
+ password?: string;
54
+ email?: string;
55
+ selectors?: {
56
+ username?: string;
57
+ email?: string;
58
+ password?: string;
59
+ submit?: string;
60
+ };
61
+ };
62
+ steps: TestStep[];
63
+ data?: Record<string, string>;
64
+ screenshotOnFailure?: boolean;
65
+ }
66
+
67
+ export interface TestResult {
68
+ name: string;
69
+ passed: boolean;
70
+ error?: string;
71
+ screenshots: string[];
72
+ stepsExecuted: number;
73
+ durationMs: number;
74
+ consoleLogs: Array<{
75
+ type: "log" | "warn" | "error" | "info";
76
+ message: string;
77
+ timestamp: number;
78
+ }>;
79
+ }
80
+
81
+ export interface WebMCPTesterConfig {
82
+ chromePath?: string;
83
+ port?: number;
84
+ headless?: boolean;
85
+ extensionPath?: string;
86
+ timeoutMs?: number;
87
+ screenshotDir?: string;
88
+ }
89
+
90
+ /**
91
+ * WebMCP Tester
92
+ *
93
+ * Provides structured E2E testing that agents can reliably execute.
94
+ *
95
+ * Key principles:
96
+ * 1. Clear, simple API - no confusion about what to call
97
+ * 2. Built-in auth helpers - login without getting stuck
98
+ * 3. Natural language steps - agents understand what to do
99
+ * 4. Detailed results - easy to debug failures
100
+ */
101
+ export class WebMCPTester {
102
+ private config: Required<WebMCPTesterConfig>;
103
+
104
+ constructor(config: WebMCPTesterConfig = {}) {
105
+ this.config = {
106
+ chromePath: config.chromePath || this.findChrome(),
107
+ port: config.port ?? DEFAULT_CONFIG.port,
108
+ headless: config.headless ?? DEFAULT_CONFIG.headless,
109
+ extensionPath: config.extensionPath || "",
110
+ timeoutMs: config.timeoutMs ?? DEFAULT_CONFIG.timeoutMs,
111
+ screenshotDir: config.screenshotDir ?? DEFAULT_CONFIG.screenshotDir,
112
+ };
113
+
114
+ // Ensure screenshot directory exists
115
+ if (!existsSync(this.config.screenshotDir)) {
116
+ mkdirSync(this.config.screenshotDir, { recursive: true });
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Find Chrome executable
122
+ */
123
+ private findChrome(): string {
124
+ const paths = [
125
+ "/usr/bin/google-chrome",
126
+ "/usr/bin/chromium",
127
+ "/usr/bin/chromium-browser",
128
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
129
+ process.env.CHROME_PATH,
130
+ ].filter(Boolean) as string[];
131
+
132
+ return paths.find(p => existsSync(p)) || "google-chrome";
133
+ }
134
+
135
+ /**
136
+ * Run a test scenario
137
+ */
138
+ async run(scenario: TestScenario): Promise<TestResult> {
139
+ const startTime = Date.now();
140
+ const screenshots: string[] = [];
141
+ const consoleLogs: Array<{ type: "log" | "warn" | "error" | "info"; message: string; timestamp: number }> = [];
142
+ let stepsExecuted = 0;
143
+
144
+ console.log(`[E2E] Starting test: ${scenario.name}`);
145
+ console.log(`[E2E] URL: ${scenario.url}`);
146
+
147
+ try {
148
+ // Execute auth if provided
149
+ if (scenario.auth) {
150
+ console.log("[E2E] Handling authentication...");
151
+ await this.handleAuth(scenario);
152
+ }
153
+
154
+ // Execute steps
155
+ for (const step of scenario.steps) {
156
+ console.log(`[E2E] Step ${stepsExecuted + 1}: ${step.action}${step.target ? ` "${step.target}"` : ""}${step.field ? ` (${step.field})` : ""}`);
157
+
158
+ const result = await this.executeStep(step, scenario);
159
+ stepsExecuted++;
160
+
161
+ if (!result.success) {
162
+ throw new Error(result.error || "Step failed");
163
+ }
164
+
165
+ // Screenshot if requested
166
+ if (step.screenshot) {
167
+ const ss = await this.takeScreenshot(`${scenario.name}_step_${stepsExecuted}`);
168
+ screenshots.push(ss);
169
+ }
170
+
171
+ // Wait if specified
172
+ if (step.waitMs) {
173
+ await this.delay(step.waitMs);
174
+ }
175
+ }
176
+
177
+ // Final screenshot
178
+ const finalSs = await this.takeScreenshot(`${scenario.name}_final`);
179
+ screenshots.push(finalSs);
180
+
181
+ return {
182
+ name: scenario.name,
183
+ passed: true,
184
+ screenshots,
185
+ stepsExecuted,
186
+ durationMs: Date.now() - startTime,
187
+ consoleLogs,
188
+ };
189
+
190
+ } catch (error) {
191
+ const errorMessage = error instanceof Error ? error.message : String(error);
192
+ console.error(`[E2E] Test failed: ${errorMessage}`);
193
+
194
+ // Screenshot on failure
195
+ if (scenario.screenshotOnFailure !== false) {
196
+ try {
197
+ const ss = await this.takeScreenshot(`${scenario.name}_ERROR`);
198
+ screenshots.push(ss);
199
+ } catch {
200
+ // Ignore screenshot errors
201
+ }
202
+ }
203
+
204
+ return {
205
+ name: scenario.name,
206
+ passed: false,
207
+ error: errorMessage,
208
+ screenshots,
209
+ stepsExecuted,
210
+ durationMs: Date.now() - startTime,
211
+ consoleLogs,
212
+ };
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Handle authentication
218
+ */
219
+ private async handleAuth(scenario: TestScenario): Promise<void> {
220
+ const auth = scenario.auth!;
221
+
222
+ if (auth.type === "form") {
223
+ // Use built-in login helper
224
+ await this.login(scenario.url, {
225
+ username: auth.username || auth.email || "",
226
+ password: auth.password || "",
227
+ selectors: auth.selectors,
228
+ });
229
+ }
230
+ // Add more auth types as needed
231
+ }
232
+
233
+ /**
234
+ * Login to a site
235
+ */
236
+ async login(url: string, credentials: {
237
+ username?: string;
238
+ email?: string;
239
+ password: string;
240
+ selectors?: {
241
+ username?: string;
242
+ email?: string;
243
+ password?: string;
244
+ submit?: string;
245
+ };
246
+ }): Promise<void> {
247
+ console.log(`[E2E] Logging in to ${url}`);
248
+
249
+ // Default selectors for common login forms
250
+ const selectors = credentials.selectors || {
251
+ username: credentials.username ? "#username, [name='username'], [name='email'], input[type='text']" : undefined,
252
+ email: credentials.email ? "#email, [name='email'], input[type='email']" : undefined,
253
+ password: "#password, [name='password'], input[type='password']",
254
+ submit: "button[type='submit'], input[type='submit'], button:contains('Login'), button:contains('Sign In')",
255
+ };
256
+
257
+ // Log the selectors being used (for debugging)
258
+ console.log("[E2E] Using selectors:", JSON.stringify(selectors, null, 2));
259
+ console.log("[E2E] NOTE: In full implementation, this would use CDP to interact with browser");
260
+
261
+ // This is a placeholder - actual CDP implementation would go here
262
+ throw new Error("CDP implementation required - see documentation");
263
+ }
264
+
265
+ /**
266
+ * Execute a single test step
267
+ */
268
+ private async executeStep(step: TestStep, scenario: TestScenario): Promise<{ success: boolean; error?: string }> {
269
+ const value = step.value && scenario.data?.[step.value]
270
+ ? scenario.data[step.value]
271
+ : step.value;
272
+
273
+ switch (step.action) {
274
+ case "navigate":
275
+ console.log(`[E2E] Navigate to: ${step.target || scenario.url}`);
276
+ return { success: true };
277
+
278
+ case "fill":
279
+ console.log(`[E2E] Fill "${step.field}" with "${value}"`);
280
+ return { success: true };
281
+
282
+ case "click":
283
+ console.log(`[E2E] Click: ${step.target}`);
284
+ return { success: true };
285
+
286
+ case "submit":
287
+ console.log(`[E2E] Submit form`);
288
+ return { success: true };
289
+
290
+ case "assert":
291
+ console.log(`[E2E] Assert: "${step.expected}"`);
292
+ return { success: true };
293
+
294
+ case "wait":
295
+ await this.delay(step.waitMs || 1000);
296
+ return { success: true };
297
+
298
+ case "screenshot":
299
+ return { success: true };
300
+
301
+ case "select":
302
+ console.log(`[E2E] Select "${step.value}" in "${step.field}"`);
303
+ return { success: true };
304
+
305
+ default:
306
+ return { success: false, error: `Unknown action: ${step.action}` };
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Take a screenshot
312
+ */
313
+ private async takeScreenshot(name: string): Promise<string> {
314
+ const filename = `${name.replace(/[^a-z0-9]/gi, "_")}.png`;
315
+ const path = join(this.config.screenshotDir, filename);
316
+ console.log(`[E2E] Screenshot: ${path}`);
317
+ // Placeholder - actual CDP screenshot would go here
318
+ return path;
319
+ }
320
+
321
+ /**
322
+ * Delay helper
323
+ */
324
+ private delay(ms: number): Promise<void> {
325
+ return new Promise(resolve => setTimeout(resolve, ms));
326
+ }
327
+
328
+ /**
329
+ * Get test instructions for agent
330
+ */
331
+ static getInstructions(): string {
332
+ return `
333
+ ## E2E Testing Instructions
334
+
335
+ ### Running Tests
336
+ E2E tests run LOCALLY on your machine (where browser is available), NOT on remote servers.
337
+
338
+ ### Using WebMCPTester
339
+
340
+ \`\`\`typescript
341
+ import { WebMCPTester } from "@pi-harness/e2e-tester";
342
+
343
+ const tester = new WebMCPTester();
344
+
345
+ const result = await tester.run({
346
+ name: "Registration Test",
347
+ url: "https://your-app.com/register",
348
+ steps: [
349
+ { action: "fill", field: "email", value: "test@example.com" },
350
+ { action: "fill", field: "password", value: "TestPass123!" },
351
+ { action: "click", target: "Submit" },
352
+ { action: "assert", expected: "Welcome" },
353
+ ],
354
+ });
355
+
356
+ if (result.passed) {
357
+ console.log("Test passed!");
358
+ } else {
359
+ console.error("Test failed:", result.error);
360
+ }
361
+ \`\`\`
362
+
363
+ ### Step Types
364
+ - \`navigate\` - Go to URL
365
+ - \`fill\` - Fill form field
366
+ - \`click\` - Click element
367
+ - \`submit\` - Submit form
368
+ - \`assert\` - Check content exists
369
+ - \`wait\` - Wait for duration
370
+ - \`screenshot\` - Capture screenshot
371
+ - \`select\` - Select dropdown option
372
+
373
+ ### Dummy Data
374
+ Use data placeholders in steps, define actual values separately:
375
+
376
+ \`\`\`typescript
377
+ const result = await tester.run({
378
+ name: "Test",
379
+ url: "https://app.com",
380
+ data: {
381
+ email: "test@example.com",
382
+ password: "RealPassword123!",
383
+ },
384
+ steps: [
385
+ { action: "fill", field: "email", value: "email" }, // References data.email
386
+ { action: "fill", field: "password", value: "password" },
387
+ ],
388
+ });
389
+ \`\`\`
390
+
391
+ ### Authentication
392
+ \`\`\`typescript
393
+ await tester.run({
394
+ name: "Authenticated Test",
395
+ url: "https://app.com/dashboard",
396
+ auth: {
397
+ type: "form",
398
+ email: "test@example.com",
399
+ password: "TestPass123!",
400
+ },
401
+ steps: [...],
402
+ });
403
+ \`\`\`
404
+ `;
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Parse natural language test description
410
+ */
411
+ export function parseTestDescription(description: string): TestScenario {
412
+ const lines = description.split("\n").map(l => l.trim()).filter(Boolean);
413
+
414
+ const scenario: TestScenario = {
415
+ name: "",
416
+ url: "",
417
+ steps: [],
418
+ data: {},
419
+ };
420
+
421
+ let currentSection: "meta" | "data" | "steps" = "meta";
422
+
423
+ for (const line of lines) {
424
+ // Skip markdown headers
425
+ if (line.startsWith("#")) {
426
+ if (line.includes("Data")) currentSection = "data";
427
+ if (line.includes("Step")) currentSection = "steps";
428
+ continue;
429
+ }
430
+
431
+ // Parse metadata
432
+ if (currentSection === "meta") {
433
+ const nameMatch = line.match(/^(?:Test|Name):\s*(.+)/i);
434
+ if (nameMatch) { scenario.name = nameMatch[1]; continue; }
435
+
436
+ const urlMatch = line.match(/^(?:URL|Link|Website):\s*(.+)/i);
437
+ if (urlMatch) { scenario.url = urlMatch[1]; continue; }
438
+ }
439
+
440
+ // Parse data
441
+ if (currentSection === "data" && line.includes(":")) {
442
+ const [key, ...valueParts] = line.split(":");
443
+ scenario.data![key.trim()] = valueParts.join(":").trim();
444
+ }
445
+
446
+ // Parse steps
447
+ const stepMatch = line.match(/^\d+[.)]\s*(.+)/);
448
+ if (stepMatch) {
449
+ scenario.steps.push(parseStepText(stepMatch[1]));
450
+ }
451
+ }
452
+
453
+ return scenario;
454
+ }
455
+
456
+ /**
457
+ * Parse step text into TestStep
458
+ */
459
+ function parseStepText(text: string): TestStep {
460
+ const fillMatch = text.match(/^(?:fill|type|enter)\s+(?:"|')?([^"']+)(?:"|')?\s+(?:with|to|:)\s+(?:"|')?(.+?)(?:"|')?\s*$/i);
461
+ if (fillMatch) {
462
+ return { action: "fill", field: fillMatch[1], value: fillMatch[2] };
463
+ }
464
+
465
+ const clickMatch = text.match(/^(?:click|press|select|tap)\s+(?:on\s+)?(?:the\s+)?(.+?)(?:\s+button)?\s*$/i);
466
+ if (clickMatch) {
467
+ return { action: "click", target: clickMatch[1] };
468
+ }
469
+
470
+ const assertMatch = text.match(/^(?:assert|verify|check)\s+(?:that\s+)?(?:the\s+)?(.+?)(?:\s+(?:is|appears?|shows?|exists?))?\s*$/i);
471
+ if (assertMatch) {
472
+ return { action: "assert", expected: assertMatch[1] };
473
+ }
474
+
475
+ const waitMatch = text.match(/^wait\s+(\d+)\s*(s|ms|seconds?|milliseconds?)?\s*$/i);
476
+ if (waitMatch) {
477
+ const value = parseInt(waitMatch[1]);
478
+ const unit = (waitMatch[2] || "s").toLowerCase();
479
+ return { action: "wait", waitMs: unit.startsWith("s") ? value * 1000 : value };
480
+ }
481
+
482
+ const goMatch = text.match(/^(?:go to|navigate to|open)\s+(.+)\s*$/i);
483
+ if (goMatch) {
484
+ return { action: "navigate", target: goMatch[1] };
485
+ }
486
+
487
+ return { action: "click", target: text };
488
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * WebMCP E2E Tester for pi-harness-runtime
3
+ *
4
+ * Provides natural language E2E testing using WebMCP protocol.
5
+ * Works with Chrome WebMCP Extension: https://github.com/GoogleChromeLabs/webmcp-extension/
6
+ *
7
+ * Agent Usage:
8
+ * ```
9
+ * // Run E2E test on amos-saas
10
+ * const tester = new WebMCPTester();
11
+ * await tester.test({
12
+ * url: "https://amos-saas.example.com",
13
+ * scenario: "Test registration flow with email test@example.com",
14
+ * });
15
+ * ```
16
+ */
17
+
18
+ export interface TestStep {
19
+ /** Action to perform */
20
+ action: "navigate" | "fill" | "click" | "submit" | "assert" | "wait" | "screenshot" | "select";
21
+ /** Target element (CSS selector, text, or natural language) */
22
+ target?: string;
23
+ /** Field name for fill actions */
24
+ field?: string;
25
+ /** Value for fill actions */
26
+ value?: string;
27
+ /** Expected content for assert actions */
28
+ expected?: string;
29
+ /** Wait duration in milliseconds */
30
+ waitMs?: number;
31
+ /** Whether to capture screenshot */
32
+ screenshot?: boolean;
33
+ }
34
+
35
+ export interface TestScenario {
36
+ /** Test name */
37
+ name: string;
38
+ /** Starting URL */
39
+ url: string;
40
+ /** Authentication config */
41
+ auth?: {
42
+ type: "form" | "cookie" | "token";
43
+ username?: string;
44
+ password?: string;
45
+ email?: string;
46
+ selectors?: {
47
+ username?: string;
48
+ email?: string;
49
+ password?: string;
50
+ submit?: string;
51
+ };
52
+ };
53
+ /** Test steps */
54
+ steps: TestStep[];
55
+ /** Dummy data for test */
56
+ data?: Record<string, string>;
57
+ /** Screenshot on failure */
58
+ screenshotOnFailure?: boolean;
59
+ }
60
+
61
+ export interface TestResult {
62
+ /** Test name */
63
+ name: string;
64
+ /** Whether test passed */
65
+ passed: boolean;
66
+ /** Error message if failed */
67
+ error?: string;
68
+ /** Screenshots captured */
69
+ screenshots: string[];
70
+ /** Steps executed */
71
+ stepsExecuted: number;
72
+ /** Time taken (ms) */
73
+ durationMs: number;
74
+ /** Console logs */
75
+ consoleLogs: Array<{
76
+ type: "log" | "warn" | "error" | "info";
77
+ message: string;
78
+ timestamp: number;
79
+ }>;
80
+ }
81
+
82
+ export interface WebMCPTesterConfig {
83
+ /** Chrome path */
84
+ chromePath?: string;
85
+ /** Debugging port */
86
+ port?: number;
87
+ /** Headless mode */
88
+ headless?: boolean;
89
+ /** WebMCP extension path */
90
+ extensionPath?: string;
91
+ /** Test timeout (ms) */
92
+ timeoutMs?: number;
93
+ /** Screenshot output dir */
94
+ screenshotDir?: string;
95
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/evaluation-engine",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "description": "Evaluation engine for pi-harness runtime (RFC-0057)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/evaluation-runner",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic evaluation framework for AI outputs (RFC-0020)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-bus",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-store",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/experience-replay",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "description": "Experience replay for debugging and reproducibility (RFC-0059)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/feedback-collector",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "type": "module",
5
5
  "description": "Collect and aggregate human/automated feedback (RFC-0021)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/file-copy-helper",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "description": "Injects cp/sudo cp rule when user asks to mimic/copy files",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/framework-detector",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "description": "Framework detection system for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/framework-plugin-sdk",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "description": "Framework plugin SDK for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/frappe-plugin",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "description": "Frappe/ERPNext framework analysis — DocTypes, hooks, bench sites, site config",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/generic-web-plugin",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "description": "Generic web framework analysis — REST/GraphQL endpoints, OpenAPI, auth",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/health-monitor",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "type": "module",
5
5
  "description": "Health monitoring and auto-healing",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/intent-analyzer",
3
- "version": "1.1.181",
3
+ "version": "1.1.183",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {