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,84 @@
1
+ /**
2
+ * Selector resolution and page discovery
3
+ * Converts string selectors to Playwright Locators and discovers active pages
4
+ */
5
+ /**
6
+ * Convert a selector string to a Playwright Locator.
7
+ *
8
+ * Supported formats:
9
+ * - CSS selectors: #id, .class, div > span (passed directly to Playwright)
10
+ * - Text: text=Click me (Playwright native)
11
+ * - Role: role=button[name='Submit'] (Playwright native)
12
+ * - XPath: xpath=//div[@id='main'] (Playwright native)
13
+ * - Test ID: testid=my-btn (resolved via getByTestId)
14
+ */
15
+ export function resolveSelector(page, selector) {
16
+ // testid= is the one prefix Playwright doesn't handle natively
17
+ if (selector.startsWith("testid=")) {
18
+ const testId = selector.slice("testid=".length);
19
+ return page.getByTestId(testId);
20
+ }
21
+ // All other selectors: CSS, text=, role=, xpath= — Playwright handles natively
22
+ return page.locator(selector);
23
+ }
24
+ /**
25
+ * Find the active page for interaction in a session.
26
+ *
27
+ * - If pageIdentifier is provided, looks up that specific page
28
+ * - If omitted and session has exactly one page, auto-selects it
29
+ * - If omitted and session has 0 or >1 pages, returns actionable error
30
+ */
31
+ export function getActivePage(sessionManager, sessionId, pageIdentifier) {
32
+ // Validate session exists
33
+ const session = sessionManager.get(sessionId);
34
+ if (!session) {
35
+ return {
36
+ success: false,
37
+ error: `Session not found: ${sessionId}`,
38
+ };
39
+ }
40
+ if (pageIdentifier) {
41
+ // Look up specific page by identifier
42
+ const ref = sessionManager.getPageRef(sessionId, pageIdentifier);
43
+ if (!ref) {
44
+ const refs = sessionManager.getPageRefs(sessionId);
45
+ const available = refs.map((r) => r.type === "electron" ? "electron" : r.url ?? "unknown");
46
+ return {
47
+ success: false,
48
+ error: `Page not found: ${pageIdentifier}`,
49
+ availablePages: available.length > 0 ? available : undefined,
50
+ };
51
+ }
52
+ return {
53
+ success: true,
54
+ page: ref.page,
55
+ identifier: pageIdentifier,
56
+ type: ref.type,
57
+ };
58
+ }
59
+ // Auto-discover: no pageIdentifier provided
60
+ const refs = sessionManager.getPageRefs(sessionId);
61
+ if (refs.length === 0) {
62
+ return {
63
+ success: false,
64
+ error: "No pages available in this session. Launch an app first with launch_web_server, launch_electron, or screenshot_web.",
65
+ };
66
+ }
67
+ if (refs.length === 1) {
68
+ const ref = refs[0];
69
+ const identifier = ref.type === "electron" ? "electron" : ref.url ?? "unknown";
70
+ return {
71
+ success: true,
72
+ page: ref.page,
73
+ identifier,
74
+ type: ref.type,
75
+ };
76
+ }
77
+ // Multiple pages — require explicit selection
78
+ const available = refs.map((r) => r.type === "electron" ? "electron" : r.url ?? "unknown");
79
+ return {
80
+ success: false,
81
+ error: `Multiple pages found (${refs.length}). Specify pageIdentifier to target a specific page.`,
82
+ availablePages: available,
83
+ };
84
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Interaction-specific type definitions
3
+ * Used by click_element, type_text, and navigation tools
4
+ */
5
+ import type { Page } from "playwright";
6
+ /** Options for click_element tool */
7
+ export interface ClickOptions {
8
+ button?: "left" | "right" | "middle";
9
+ clickCount?: number;
10
+ position?: {
11
+ x: number;
12
+ y: number;
13
+ };
14
+ force?: boolean;
15
+ timeout?: number;
16
+ }
17
+ /** Options for type_text tool */
18
+ export interface TypeOptions {
19
+ pressSequentially?: boolean;
20
+ delay?: number;
21
+ clear?: boolean;
22
+ timeout?: number;
23
+ }
24
+ /** Navigate tool actions */
25
+ export type NavigateAction = "goto" | "back" | "forward";
26
+ /** Result of finding the active page for interaction */
27
+ export type PageDiscoveryResult = {
28
+ success: true;
29
+ page: Page;
30
+ identifier: string;
31
+ type: "web" | "electron";
32
+ } | {
33
+ success: false;
34
+ error: string;
35
+ availablePages?: string[];
36
+ };
37
+ /** State values for locator.waitFor() */
38
+ export type WaitForState = "visible" | "hidden" | "attached" | "detached";
39
+ /** Shape of the get_element_state tool response */
40
+ export interface ElementStateResult {
41
+ selector: string;
42
+ visible: boolean;
43
+ enabled: boolean;
44
+ editable: boolean;
45
+ checked: boolean | null;
46
+ textContent: string | null;
47
+ innerText: string;
48
+ inputValue: string | null;
49
+ attributes: Record<string, string | null>;
50
+ boundingBox: {
51
+ x: number;
52
+ y: number;
53
+ width: number;
54
+ height: number;
55
+ } | null;
56
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Interaction-specific type definitions
3
+ * Used by click_element, type_text, and navigation tools
4
+ */
5
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Process tree cleanup utility
3
+ * Wraps tree-kill with timeout for best-effort process termination
4
+ */
5
+ import { ChildProcess } from "child_process";
6
+ import { Resource } from "../types/index.js";
7
+ import { ProcessType } from "./types.js";
8
+ /**
9
+ * Kill a process and all its children (best-effort)
10
+ * Always resolves -- cleanup should never block shutdown
11
+ *
12
+ * @param pid - OS process ID to kill
13
+ * @param timeoutMs - Maximum time to wait for kill (default 5000ms)
14
+ */
15
+ export declare function killProcessTree(pid: number, timeoutMs?: number): Promise<void>;
16
+ /**
17
+ * Create a Resource that cleans up a child process on disposal
18
+ * Integrates with SessionManager's resource tracking
19
+ *
20
+ * @param childProcess - The spawned child process
21
+ * @param _type - Process type (for future use in type-specific cleanup)
22
+ */
23
+ export declare function createProcessResource(childProcess: ChildProcess, _type: ProcessType): Resource;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Process tree cleanup utility
3
+ * Wraps tree-kill with timeout for best-effort process termination
4
+ */
5
+ import treeKill from "tree-kill";
6
+ /**
7
+ * Kill a process and all its children (best-effort)
8
+ * Always resolves -- cleanup should never block shutdown
9
+ *
10
+ * @param pid - OS process ID to kill
11
+ * @param timeoutMs - Maximum time to wait for kill (default 5000ms)
12
+ */
13
+ export function killProcessTree(pid, timeoutMs = 5000) {
14
+ return new Promise((resolve) => {
15
+ const timeout = setTimeout(() => {
16
+ console.error(`[Process] Tree-kill timeout after ${timeoutMs}ms for PID ${pid}, continuing anyway`);
17
+ resolve();
18
+ }, timeoutMs);
19
+ treeKill(pid, "SIGTERM", (error) => {
20
+ clearTimeout(timeout);
21
+ if (error) {
22
+ console.error(`[Process] Tree-kill error for PID ${pid}: ${error.message} (process may already be dead)`);
23
+ }
24
+ else {
25
+ console.error(`[Process] Killed process tree for PID ${pid}`);
26
+ }
27
+ resolve();
28
+ });
29
+ });
30
+ }
31
+ /**
32
+ * Create a Resource that cleans up a child process on disposal
33
+ * Integrates with SessionManager's resource tracking
34
+ *
35
+ * @param childProcess - The spawned child process
36
+ * @param _type - Process type (for future use in type-specific cleanup)
37
+ */
38
+ export function createProcessResource(childProcess, _type) {
39
+ return {
40
+ cleanup: async () => {
41
+ // Process already exited -- nothing to clean up
42
+ if (childProcess.exitCode !== null) {
43
+ return;
44
+ }
45
+ if (childProcess.pid !== undefined) {
46
+ await killProcessTree(childProcess.pid);
47
+ }
48
+ },
49
+ };
50
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Cross-platform process spawning utility
3
+ * Handles Windows quirks (cmd scripts, npm wrappers) transparently
4
+ */
5
+ import { ChildProcess, SpawnOptions } from "child_process";
6
+ /**
7
+ * Spawn a process with cross-platform compatibility
8
+ * Automatically enables shell mode on Windows for npm/npx and .cmd/.bat files
9
+ *
10
+ * @param command - Command to execute
11
+ * @param args - Arguments for the command
12
+ * @param options - Additional spawn options
13
+ */
14
+ export declare function spawnCrossPlatform(command: string, args: string[], options?: SpawnOptions): ChildProcess;
15
+ /**
16
+ * Attach logging listeners to a child process
17
+ * All output is routed to console.error to avoid corrupting stdio transport
18
+ *
19
+ * @param child - The child process to monitor
20
+ * @param label - Label for log messages (e.g., "web-server")
21
+ */
22
+ export declare function attachProcessListeners(child: ChildProcess, label: string): void;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Cross-platform process spawning utility
3
+ * Handles Windows quirks (cmd scripts, npm wrappers) transparently
4
+ */
5
+ import { spawn } from "child_process";
6
+ /**
7
+ * Spawn a process with cross-platform compatibility
8
+ * Automatically enables shell mode on Windows for npm/npx and .cmd/.bat files
9
+ *
10
+ * @param command - Command to execute
11
+ * @param args - Arguments for the command
12
+ * @param options - Additional spawn options
13
+ */
14
+ export function spawnCrossPlatform(command, args, options = {}) {
15
+ const isWindows = process.platform === "win32";
16
+ let useShell = options.shell ?? false;
17
+ if (isWindows) {
18
+ const lowerCmd = command.toLowerCase();
19
+ // npm/npx on Windows are .cmd files that require shell
20
+ if (lowerCmd === "npm" ||
21
+ lowerCmd === "npx" ||
22
+ lowerCmd.endsWith(".cmd") ||
23
+ lowerCmd.endsWith(".bat")) {
24
+ useShell = true;
25
+ }
26
+ }
27
+ return spawn(command, args, {
28
+ ...options,
29
+ shell: useShell,
30
+ windowsHide: true,
31
+ stdio: options.stdio || "pipe",
32
+ });
33
+ }
34
+ /**
35
+ * Attach logging listeners to a child process
36
+ * All output is routed to console.error to avoid corrupting stdio transport
37
+ *
38
+ * @param child - The child process to monitor
39
+ * @param label - Label for log messages (e.g., "web-server")
40
+ */
41
+ export function attachProcessListeners(child, label) {
42
+ child.stdout?.on("data", (data) => {
43
+ console.error(`[${label} stdout] ${data.toString().trim()}`);
44
+ });
45
+ child.stderr?.on("data", (data) => {
46
+ console.error(`[${label} stderr] ${data.toString().trim()}`);
47
+ });
48
+ child.on("error", (err) => {
49
+ console.error(`[${label}] Spawn error: ${err.message}`);
50
+ });
51
+ child.on("exit", (code, signal) => {
52
+ console.error(`[${label}] Exited: code=${code}, signal=${signal}`);
53
+ });
54
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Server readiness detection
3
+ * Uses dual strategy: stdout pattern matching + TCP port polling
4
+ */
5
+ import { ChildProcess } from "child_process";
6
+ /**
7
+ * Detect when a spawned server is ready to accept connections
8
+ * Races two strategies: stdout pattern matching and TCP port availability
9
+ *
10
+ * @param child - The spawned server process
11
+ * @param port - Expected port the server will listen on
12
+ * @param timeoutMs - Maximum time to wait for readiness (default 60000ms)
13
+ */
14
+ export declare function detectServerReady(child: ChildProcess, port: number, timeoutMs?: number): Promise<void>;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Server readiness detection
3
+ * Uses dual strategy: stdout pattern matching + TCP port polling
4
+ */
5
+ import waitOn from "wait-on";
6
+ /**
7
+ * Common server ready patterns across popular dev servers
8
+ */
9
+ const readyPatterns = [
10
+ /Local:.*https?:\/\/localhost[:\d]*/i, // Vite
11
+ /webpack.*compiled/i, // webpack
12
+ /ready.*started.*server.*on/i, // Next.js
13
+ /server.*(?:listening|running|started).*:\d+/i, // generic
14
+ /ready on/i, // Next.js alt
15
+ /compiled successfully/i, // CRA
16
+ ];
17
+ /**
18
+ * Detect when a spawned server is ready to accept connections
19
+ * Races two strategies: stdout pattern matching and TCP port availability
20
+ *
21
+ * @param child - The spawned server process
22
+ * @param port - Expected port the server will listen on
23
+ * @param timeoutMs - Maximum time to wait for readiness (default 60000ms)
24
+ */
25
+ export function detectServerReady(child, port, timeoutMs = 60000) {
26
+ // Strategy 1: stdout/stderr pattern matching
27
+ const stdoutReady = new Promise((resolve, reject) => {
28
+ const checkOutput = (data) => {
29
+ const text = data.toString();
30
+ for (const pattern of readyPatterns) {
31
+ if (pattern.test(text)) {
32
+ cleanup();
33
+ resolve();
34
+ return;
35
+ }
36
+ }
37
+ };
38
+ const onExit = (code) => {
39
+ cleanup();
40
+ reject(new Error(`Server process exited before becoming ready (exit code: ${code})`));
41
+ };
42
+ const cleanup = () => {
43
+ child.stdout?.removeListener("data", checkOutput);
44
+ child.stderr?.removeListener("data", checkOutput);
45
+ child.removeListener("exit", onExit);
46
+ };
47
+ child.stdout?.on("data", checkOutput);
48
+ child.stderr?.on("data", checkOutput);
49
+ child.on("exit", onExit);
50
+ });
51
+ // Strategy 2: TCP port polling via wait-on
52
+ const portReady = waitOn({
53
+ resources: [`tcp:localhost:${port}`],
54
+ timeout: timeoutMs,
55
+ interval: 500,
56
+ log: false,
57
+ });
58
+ return Promise.race([stdoutReady, portReady]).then(() => {
59
+ console.error(`[Monitor] Server ready on port ${port}`);
60
+ }, (error) => {
61
+ // Provide better error context if the process has already exited
62
+ if (child.exitCode !== null) {
63
+ throw new Error(`Server process exited with code ${child.exitCode} before becoming ready on port ${port}`);
64
+ }
65
+ throw error;
66
+ });
67
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Process management type definitions
3
+ * Contract for all process resources tracked by the feedback server
4
+ */
5
+ /**
6
+ * Types of processes the server can manage
7
+ */
8
+ export type ProcessType = "web-server" | "electron" | "windows-exe";
9
+ /**
10
+ * Lifecycle status of a managed process
11
+ */
12
+ export type ProcessStatus = "launching" | "ready" | "running" | "stopped" | "error";
13
+ /**
14
+ * Runtime information for a tracked process
15
+ */
16
+ export interface ProcessInfo {
17
+ /** Unique identifier (session ID + process type or similar) */
18
+ id: string;
19
+ /** The session this process belongs to */
20
+ sessionId: string;
21
+ /** Process type */
22
+ type: ProcessType;
23
+ /** Current lifecycle status */
24
+ status: ProcessStatus;
25
+ /** OS process ID (undefined if not yet spawned) */
26
+ pid: number | undefined;
27
+ /** The command that was used to launch */
28
+ command: string;
29
+ /** Arguments passed to the command */
30
+ args: string[];
31
+ /** Working directory */
32
+ cwd: string;
33
+ /** Port number (for web servers) */
34
+ port?: number;
35
+ /** When the process was started */
36
+ startedAt: Date;
37
+ /** When readiness was detected */
38
+ readyAt?: Date;
39
+ /** Exit code if process has stopped */
40
+ exitCode?: number | null;
41
+ }
42
+ /**
43
+ * Configuration for launching a web server process
44
+ */
45
+ export interface LaunchWebServerConfig {
46
+ /** Session this process belongs to */
47
+ sessionId: string;
48
+ /** Command to execute (e.g., "npm", "npx") */
49
+ command: string;
50
+ /** Arguments (e.g., ["run", "dev"], ["vite"]) */
51
+ args: string[];
52
+ /** Working directory for the project */
53
+ cwd: string;
54
+ /** Expected port the server will listen on */
55
+ port: number;
56
+ /** Readiness timeout in ms (default 60000) */
57
+ timeoutMs?: number;
58
+ }
59
+ /**
60
+ * Configuration for launching an Electron application
61
+ */
62
+ export interface LaunchElectronConfig {
63
+ /** Session this process belongs to */
64
+ sessionId: string;
65
+ /** Path to Electron main entry file */
66
+ entryPath: string;
67
+ /** Optional working directory */
68
+ cwd?: string;
69
+ /** Launch timeout in ms (default 30000) */
70
+ timeoutMs?: number;
71
+ }
72
+ /**
73
+ * Configuration for launching a Windows executable
74
+ */
75
+ export interface LaunchWindowsExeConfig {
76
+ /** Session this process belongs to */
77
+ sessionId: string;
78
+ /** Path to the .exe file */
79
+ exePath: string;
80
+ /** Optional command line arguments */
81
+ args?: string[];
82
+ /** Optional working directory */
83
+ cwd?: string;
84
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Process management type definitions
3
+ * Contract for all process resources tracked by the feedback server
4
+ */
5
+ export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Auto-capture module
3
+ * Listens for Playwright page navigation events and captures screenshots automatically
4
+ */
5
+ import type { Page } from "playwright";
6
+ import type { SessionManager } from "../session-manager.js";
7
+ /**
8
+ * Attach auto-capture listener to a Playwright page.
9
+ * Captures a screenshot after every main-frame navigation.
10
+ * Stores the optimized screenshot in the session manager.
11
+ *
12
+ * @returns Cleanup function to remove the listener
13
+ */
14
+ export declare function setupAutoCapture(page: Page, sessionId: string, sessionManager: SessionManager): () => void;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Auto-capture module
3
+ * Listens for Playwright page navigation events and captures screenshots automatically
4
+ */
5
+ import { optimizeScreenshot } from "./optimize.js";
6
+ /**
7
+ * Attach auto-capture listener to a Playwright page.
8
+ * Captures a screenshot after every main-frame navigation.
9
+ * Stores the optimized screenshot in the session manager.
10
+ *
11
+ * @returns Cleanup function to remove the listener
12
+ */
13
+ export function setupAutoCapture(page, sessionId, sessionManager) {
14
+ const handler = async (frame) => {
15
+ if (frame !== page.mainFrame())
16
+ return;
17
+ try {
18
+ await page.waitForLoadState("load");
19
+ const rawBuffer = await page.screenshot({ type: "png" });
20
+ const optimized = await optimizeScreenshot(rawBuffer);
21
+ sessionManager.setAutoCapture(sessionId, {
22
+ imageBase64: optimized.data.toString("base64"),
23
+ mimeType: optimized.mimeType,
24
+ url: page.url(),
25
+ capturedAt: new Date(),
26
+ });
27
+ console.error(`[auto-capture] Captured ${optimized.width}x${optimized.height} ` +
28
+ `(${optimized.data.length} bytes) for session ${sessionId}`);
29
+ }
30
+ catch (error) {
31
+ console.error(`[auto-capture] Failed for session ${sessionId}:`, error);
32
+ }
33
+ };
34
+ page.on("framenavigated", handler);
35
+ return () => {
36
+ page.off("framenavigated", handler);
37
+ };
38
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Core screenshot capture functions
3
+ * Playwright pages (web/Electron) and Windows desktop windows
4
+ */
5
+ import type { Page } from "playwright";
6
+ /**
7
+ * Capture a screenshot from a Playwright Page (web or Electron)
8
+ * Returns raw PNG buffer for further optimization
9
+ */
10
+ export declare function capturePlaywrightPage(page: Page, options?: {
11
+ fullPage?: boolean;
12
+ }): Promise<Buffer>;
13
+ /**
14
+ * Capture a screenshot of a Windows desktop window by PID.
15
+ * Uses node-screenshots to enumerate windows and match by process ID.
16
+ * Returns raw PNG buffer for further optimization.
17
+ *
18
+ * Note: node-screenshots Window objects may not expose PID in all versions.
19
+ * Falls back to matching by window title if PID is unavailable.
20
+ */
21
+ export declare function captureDesktopWindow(pid: number): Promise<Buffer>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Core screenshot capture functions
3
+ * Playwright pages (web/Electron) and Windows desktop windows
4
+ */
5
+ /**
6
+ * Capture a screenshot from a Playwright Page (web or Electron)
7
+ * Returns raw PNG buffer for further optimization
8
+ */
9
+ export async function capturePlaywrightPage(page, options) {
10
+ return await page.screenshot({
11
+ fullPage: options?.fullPage ?? false,
12
+ type: "png",
13
+ });
14
+ }
15
+ /**
16
+ * Capture a screenshot of a Windows desktop window by PID.
17
+ * Uses node-screenshots to enumerate windows and match by process ID.
18
+ * Returns raw PNG buffer for further optimization.
19
+ *
20
+ * Note: node-screenshots Window objects may not expose PID in all versions.
21
+ * Falls back to matching by window title if PID is unavailable.
22
+ */
23
+ export async function captureDesktopWindow(pid) {
24
+ // Dynamic import to avoid load-time failures on non-Windows
25
+ const { Window } = await import("node-screenshots");
26
+ const windows = Window.all();
27
+ // node-screenshots Window type doesn't expose pid in current typings,
28
+ // but some builds include it at runtime. Use any cast for defensive check.
29
+ const target = windows.find((w) => {
30
+ const wAny = w;
31
+ if (typeof wAny.processId === "number")
32
+ return wAny.processId === pid;
33
+ if (typeof wAny.pid === "number")
34
+ return wAny.pid === pid;
35
+ if (typeof wAny.pid === "function")
36
+ return wAny.pid() === pid;
37
+ return false;
38
+ });
39
+ if (!target) {
40
+ throw new Error(`No window found for PID ${pid}. The process may not have a visible window yet.`);
41
+ }
42
+ if (target.isMinimized) {
43
+ throw new Error(`Window for PID ${pid} is minimized. Restore it before capturing.`);
44
+ }
45
+ const image = await target.captureImage();
46
+ const pngData = await image.toPng();
47
+ return Buffer.from(pngData);
48
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Screenshot optimization pipeline
3
+ * Resizes and converts screenshots to WebP for efficient MCP transport
4
+ */
5
+ /**
6
+ * Optimize a raw screenshot buffer: resize + WebP conversion
7
+ * @param buffer - Raw PNG/JPEG buffer from capture
8
+ * @param options - Optimization options
9
+ * @returns Optimized buffer with metadata
10
+ */
11
+ export declare function optimizeScreenshot(buffer: Buffer, options?: {
12
+ maxWidth?: number;
13
+ quality?: number;
14
+ }): Promise<{
15
+ data: Buffer;
16
+ mimeType: string;
17
+ width: number;
18
+ height: number;
19
+ }>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Screenshot optimization pipeline
3
+ * Resizes and converts screenshots to WebP for efficient MCP transport
4
+ */
5
+ import sharp from "sharp";
6
+ /**
7
+ * Optimize a raw screenshot buffer: resize + WebP conversion
8
+ * @param buffer - Raw PNG/JPEG buffer from capture
9
+ * @param options - Optimization options
10
+ * @returns Optimized buffer with metadata
11
+ */
12
+ export async function optimizeScreenshot(buffer, options) {
13
+ const maxWidth = options?.maxWidth ?? 1280;
14
+ const quality = options?.quality ?? 80;
15
+ const optimized = await sharp(buffer)
16
+ .resize(maxWidth, undefined, {
17
+ fit: "inside",
18
+ withoutEnlargement: true,
19
+ })
20
+ .webp({ quality })
21
+ .toBuffer({ resolveWithObject: true });
22
+ return {
23
+ data: optimized.data,
24
+ mimeType: "image/webp",
25
+ width: optimized.info.width,
26
+ height: optimized.info.height,
27
+ };
28
+ }