@_deep4wee/agent-lens 1.0.1

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.
@@ -0,0 +1,170 @@
1
+ import { Page, BrowserContext } from 'playwright';
2
+
3
+ interface ConsoleEntry {
4
+ level: 'error' | 'warning' | 'info' | 'log' | 'debug';
5
+ text: string;
6
+ url: string;
7
+ timestamp: string;
8
+ stack?: string;
9
+ }
10
+
11
+ /**
12
+ * Generic Mock IPC Registry for Preview Mode
13
+ *
14
+ * Injects a global `window.__mockIpc` that web apps or mocked electron/tauri APIs can call.
15
+ */
16
+ type MockIpcResponseType = 'SUCCESS' | 'ERROR';
17
+
18
+ interface ViewportPreset {
19
+ name: string;
20
+ width: number;
21
+ height: number;
22
+ }
23
+ declare const VIEWPORT_PRESETS: {
24
+ /** Minimum supported window size (e.g., ) */
25
+ MIN_SUPPORTED: ViewportPreset;
26
+ /** Standard default window size (e.g., ) */
27
+ DEFAULT: ViewportPreset;
28
+ /** Wide screen for checking grids and tables */
29
+ WIDE: ViewportPreset;
30
+ /** Full HD */
31
+ FULL_HD: ViewportPreset;
32
+ };
33
+ interface CaptureOptions {
34
+ selector?: string;
35
+ fullPage?: boolean;
36
+ mask?: string[];
37
+ }
38
+ interface CaptureBurstOptions {
39
+ /** Total duration of burst animation capture in milliseconds */
40
+ durationMs: number;
41
+ /** Interval between frames in milliseconds (default: 80ms) */
42
+ intervalMs?: number;
43
+ /** Restrict capture to a specific element */
44
+ selector?: string;
45
+ }
46
+ interface SnapshotMetadata {
47
+ index: number;
48
+ name: string;
49
+ fileName: string;
50
+ filePath: string;
51
+ relativeUri: string;
52
+ viewport: {
53
+ width: number;
54
+ height: number;
55
+ };
56
+ timestamp: string;
57
+ isBurstFrame?: boolean;
58
+ burstGroup?: string;
59
+ frameIndex?: number;
60
+ selector?: string;
61
+ }
62
+ interface TestContext {
63
+ page: Page;
64
+ context: BrowserContext;
65
+ targetMode: 'desktop' | 'preview';
66
+ currentViewport: {
67
+ width: number;
68
+ height: number;
69
+ };
70
+ /** Capture a single high-quality snapshot of the window or element */
71
+ capture: (name: string, options?: CaptureOptions) => Promise<SnapshotMetadata>;
72
+ /** Capture a series of frames for animations (Framer Motion, modals, lists) */
73
+ captureBurst: (name: string, options: CaptureBurstOptions) => Promise<SnapshotMetadata[]>;
74
+ /**
75
+ * Dynamically navigate to another page during the test.
76
+ * Example: await ctx.navigate('/settings');
77
+ */
78
+ navigate: (route: string) => Promise<void>;
79
+ /** Change window / viewport size */
80
+ resize: (width: number, height: number) => Promise<void>;
81
+ /** Set one of the standard presets (MIN_SUPPORTED, DEFAULT, WIDE) */
82
+ setPreset: (preset: ViewportPreset) => Promise<void>;
83
+ /**
84
+ * Dynamically resize the window so it perfectly fits the specified element (or body).
85
+ * Useful when an agent wants to test an isolated component without background clutter.
86
+ */
87
+ resizeToFit: (selector?: string, padding?: number) => Promise<void>;
88
+ /** Wait the specified amount of milliseconds */
89
+ wait: (ms: number) => Promise<void>;
90
+ /** Wait for the selector to appear in the DOM */
91
+ waitForSelector: (selector: string, timeoutMs?: number) => Promise<void>;
92
+ /** Left-click the element */
93
+ click: (selector: string) => Promise<void>;
94
+ /** Right-click the element (Context Menu) */
95
+ rightClick: (selector: string) => Promise<void>;
96
+ /** Type text into an input field */
97
+ type: (selector: string, text: string) => Promise<void>;
98
+ /** Select an option in a <select> by its value */
99
+ selectOption: (selector: string, value: string) => Promise<void>;
100
+ /** Hover the cursor to check highlights / tooltips */
101
+ hover: (selector: string) => Promise<void>;
102
+ /** Scroll the element (or window) down by the specified number of pixels */
103
+ scroll: (selector: string, deltaY: number) => Promise<void>;
104
+ /** Log a step for the agent */
105
+ log: (message: string) => void;
106
+ /**
107
+ * Set a mock response for an IPC action.
108
+ * Changes the data returned by window.external.sendMessage in preview mode.
109
+ * Ignored in desktop mode (IPC goes through real backend).
110
+ *
111
+ * Example:
112
+ * await ctx.setMockIpc('GET_INSTANCES', [{ id: '...', name: 'Test' }]);
113
+ * await ctx.setMockIpc('CREATE_INSTANCE', 'Some error', { type: 'ERROR' });
114
+ */
115
+ setMockIpc: (action: string, data: any, options?: {
116
+ type?: MockIpcResponseType;
117
+ delayMs?: number;
118
+ }) => Promise<void>;
119
+ /** Return all intercepted console.error and pageerror logs */
120
+ getConsoleErrors: () => ConsoleEntry[];
121
+ /** Return all intercepted console.warn logs */
122
+ getConsoleWarnings: () => ConsoleEntry[];
123
+ /** Returns true if there are critical errors in the console */
124
+ hasConsoleErrors: () => boolean;
125
+ /** Get the visible text of a specific element */
126
+ readText: (selector: string) => Promise<string | null>;
127
+ /** Get ALL visible text on the page (useful for quick analysis without Vision) */
128
+ getPageText: () => Promise<string>;
129
+ /** Check if the element is visible on the screen */
130
+ isVisible: (selector: string) => Promise<boolean>;
131
+ /** Count the number of elements matching the selector */
132
+ getElementCount: (selector: string) => Promise<number>;
133
+ }
134
+ interface VisualScenario {
135
+ /** Unique scenario identifier (kebab-case) */
136
+ id: string;
137
+ /** Human-readable title for the report */
138
+ title: string;
139
+ /** Description of what is being tested */
140
+ description?: string;
141
+ /** Initial route to navigate to (e.g., '/instances', '/settings') */
142
+ route?: string;
143
+ /** List of viewport sizes to test */
144
+ viewports?: ViewportPreset[];
145
+ /**
146
+ * Initial IPC mock data for preview mode.
147
+ * Applied BEFORE navigation to the route.
148
+ */
149
+ mockIpc?: Array<{
150
+ action: string;
151
+ data: any;
152
+ type?: MockIpcResponseType;
153
+ delayMs?: number;
154
+ }>;
155
+ /**
156
+ * Optional setup hook: executed before the browser navigates and scenario runs.
157
+ * Ideal for preparing mock files, test directories, or initial state.
158
+ */
159
+ setup?: () => Promise<void> | void;
160
+ /** The main body of the scenario */
161
+ run: (ctx: TestContext) => Promise<void>;
162
+ /**
163
+ * Optional teardown hook: guaranteed to execute in finally, even if run() fails.
164
+ * Ideal for cleaning up test artifacts, cache directories, or temporary state.
165
+ */
166
+ teardown?: () => Promise<void> | void;
167
+ }
168
+ declare function defineVisualTest(scenario: VisualScenario): VisualScenario;
169
+
170
+ export { type CaptureBurstOptions, type CaptureOptions, type SnapshotMetadata, type TestContext, VIEWPORT_PRESETS, type ViewportPreset, type VisualScenario, defineVisualTest };
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/shared/api/dsl.ts
21
+ var dsl_exports = {};
22
+ __export(dsl_exports, {
23
+ VIEWPORT_PRESETS: () => VIEWPORT_PRESETS,
24
+ defineVisualTest: () => defineVisualTest
25
+ });
26
+ module.exports = __toCommonJS(dsl_exports);
27
+ var VIEWPORT_PRESETS = {
28
+ /** Minimum supported window size (e.g., ) */
29
+ MIN_SUPPORTED: { name: "min-supported", width: 1024, height: 768 },
30
+ /** Standard default window size (e.g., ) */
31
+ DEFAULT: { name: "default", width: 1200, height: 800 },
32
+ /** Wide screen for checking grids and tables */
33
+ WIDE: { name: "wide", width: 1600, height: 900 },
34
+ /** Full HD */
35
+ FULL_HD: { name: "full-hd", width: 1920, height: 1080 }
36
+ };
37
+ function defineVisualTest(scenario) {
38
+ return scenario;
39
+ }
40
+ // Annotate the CommonJS export names for ESM import in node:
41
+ 0 && (module.exports = {
42
+ VIEWPORT_PRESETS,
43
+ defineVisualTest
44
+ });
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/api/dsl.ts"],"sourcesContent":["import type { Page, BrowserContext } from 'playwright';\nimport type { ConsoleEntry } from '../../features/console-tracker/consoleTracker';\nimport type { MockIpcResponseType } from '../../features/mock-ipc/mockIpc';\n\nexport interface ViewportPreset {\n name: string;\n width: number;\n height: number;\n}\n\nexport const VIEWPORT_PRESETS = {\n /** Minimum supported window size (e.g., ) */\n MIN_SUPPORTED: { name: 'min-supported', width: 1024, height: 768 } as ViewportPreset,\n /** Standard default window size (e.g., ) */\n DEFAULT: { name: 'default', width: 1200, height: 800 } as ViewportPreset,\n /** Wide screen for checking grids and tables */\n WIDE: { name: 'wide', width: 1600, height: 900 } as ViewportPreset,\n /** Full HD */\n FULL_HD: { name: 'full-hd', width: 1920, height: 1080 } as ViewportPreset,\n};\n\nexport interface CaptureOptions {\n selector?: string;\n fullPage?: boolean;\n mask?: string[];\n}\n\nexport interface CaptureBurstOptions {\n /** Total duration of burst animation capture in milliseconds */\n durationMs: number;\n /** Interval between frames in milliseconds (default: 80ms) */\n intervalMs?: number;\n /** Restrict capture to a specific element */\n selector?: string;\n}\n\nexport interface SnapshotMetadata {\n index: number;\n name: string;\n fileName: string;\n filePath: string;\n relativeUri: string;\n viewport: { width: number; height: number };\n timestamp: string;\n isBurstFrame?: boolean;\n burstGroup?: string;\n frameIndex?: number;\n selector?: string;\n}\n\nexport interface TestContext {\n page: Page;\n context: BrowserContext;\n targetMode: 'desktop' | 'preview';\n currentViewport: { width: number; height: number };\n \n // --- Snapshots ---\n\n /** Capture a single high-quality snapshot of the window or element */\n capture: (name: string, options?: CaptureOptions) => Promise<SnapshotMetadata>;\n \n /** Capture a series of frames for animations (Framer Motion, modals, lists) */\n captureBurst: (name: string, options: CaptureBurstOptions) => Promise<SnapshotMetadata[]>;\n \n // --- Navigation ---\n\n /** \n * Dynamically navigate to another page during the test.\n * Example: await ctx.navigate('/settings');\n */\n navigate: (route: string) => Promise<void>;\n\n // --- Viewport ---\n\n /** Change window / viewport size */\n resize: (width: number, height: number) => Promise<void>;\n \n /** Set one of the standard presets (MIN_SUPPORTED, DEFAULT, WIDE) */\n setPreset: (preset: ViewportPreset) => Promise<void>;\n\n /** \n * Dynamically resize the window so it perfectly fits the specified element (or body).\n * Useful when an agent wants to test an isolated component without background clutter.\n */\n resizeToFit: (selector?: string, padding?: number) => Promise<void>;\n \n // --- Waiting ---\n\n /** Wait the specified amount of milliseconds */\n wait: (ms: number) => Promise<void>;\n \n /** Wait for the selector to appear in the DOM */\n waitForSelector: (selector: string, timeoutMs?: number) => Promise<void>;\n \n // --- Interaction ---\n\n /** Left-click the element */\n click: (selector: string) => Promise<void>;\n \n /** Right-click the element (Context Menu) */\n rightClick: (selector: string) => Promise<void>;\n \n /** Type text into an input field */\n type: (selector: string, text: string) => Promise<void>;\n \n /** Select an option in a <select> by its value */\n selectOption: (selector: string, value: string) => Promise<void>;\n \n /** Hover the cursor to check highlights / tooltips */\n hover: (selector: string) => Promise<void>;\n \n /** Scroll the element (or window) down by the specified number of pixels */\n scroll: (selector: string, deltaY: number) => Promise<void>;\n \n // --- Logging ---\n\n /** Log a step for the agent */\n log: (message: string) => void;\n\n // --- Mock IPC (preview mode only) ---\n\n /**\n * Set a mock response for an IPC action.\n * Changes the data returned by window.external.sendMessage in preview mode.\n * Ignored in desktop mode (IPC goes through real backend).\n *\n * Example:\n * await ctx.setMockIpc('GET_INSTANCES', [{ id: '...', name: 'Test' }]);\n * await ctx.setMockIpc('CREATE_INSTANCE', 'Some error', { type: 'ERROR' });\n */\n setMockIpc: (action: string, data: any, options?: { type?: MockIpcResponseType; delayMs?: number }) => Promise<void>;\n\n // --- Console Errors ---\n\n /** Return all intercepted console.error and pageerror logs */\n getConsoleErrors: () => ConsoleEntry[];\n\n /** Return all intercepted console.warn logs */\n getConsoleWarnings: () => ConsoleEntry[];\n\n /** Returns true if there are critical errors in the console */\n hasConsoleErrors: () => boolean;\n\n // --- DOM Assertions ---\n\n /** Get the visible text of a specific element */\n readText: (selector: string) => Promise<string | null>;\n \n /** Get ALL visible text on the page (useful for quick analysis without Vision) */\n getPageText: () => Promise<string>;\n \n /** Check if the element is visible on the screen */\n isVisible: (selector: string) => Promise<boolean>;\n\n /** Count the number of elements matching the selector */\n getElementCount: (selector: string) => Promise<number>;\n}\n\nexport interface VisualScenario {\n /** Unique scenario identifier (kebab-case) */\n id: string;\n /** Human-readable title for the report */\n title: string;\n /** Description of what is being tested */\n description?: string;\n /** Initial route to navigate to (e.g., '/instances', '/settings') */\n route?: string;\n /** List of viewport sizes to test */\n viewports?: ViewportPreset[];\n /**\n * Initial IPC mock data for preview mode.\n * Applied BEFORE navigation to the route.\n */\n mockIpc?: Array<{ action: string; data: any; type?: MockIpcResponseType; delayMs?: number }>;\n /**\n * Optional setup hook: executed before the browser navigates and scenario runs.\n * Ideal for preparing mock files, test directories, or initial state.\n */\n setup?: () => Promise<void> | void;\n /** The main body of the scenario */\n run: (ctx: TestContext) => Promise<void>;\n /**\n * Optional teardown hook: guaranteed to execute in finally, even if run() fails.\n * Ideal for cleaning up test artifacts, cache directories, or temporary state.\n */\n teardown?: () => Promise<void> | void;\n}\n\nexport function defineVisualTest(scenario: VisualScenario): VisualScenario {\n return scenario;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUO,IAAM,mBAAmB;AAAA;AAAA,EAE9B,eAAe,EAAE,MAAM,iBAAiB,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAEjE,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAErD,MAAM,EAAE,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAE/C,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,KAAK;AACxD;AAyKO,SAAS,iBAAiB,UAA0C;AACzE,SAAO;AACT;","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,19 @@
1
+ // src/shared/api/dsl.ts
2
+ var VIEWPORT_PRESETS = {
3
+ /** Minimum supported window size (e.g., ) */
4
+ MIN_SUPPORTED: { name: "min-supported", width: 1024, height: 768 },
5
+ /** Standard default window size (e.g., ) */
6
+ DEFAULT: { name: "default", width: 1200, height: 800 },
7
+ /** Wide screen for checking grids and tables */
8
+ WIDE: { name: "wide", width: 1600, height: 900 },
9
+ /** Full HD */
10
+ FULL_HD: { name: "full-hd", width: 1920, height: 1080 }
11
+ };
12
+ function defineVisualTest(scenario) {
13
+ return scenario;
14
+ }
15
+ export {
16
+ VIEWPORT_PRESETS,
17
+ defineVisualTest
18
+ };
19
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/api/dsl.ts"],"sourcesContent":["import type { Page, BrowserContext } from 'playwright';\nimport type { ConsoleEntry } from '../../features/console-tracker/consoleTracker';\nimport type { MockIpcResponseType } from '../../features/mock-ipc/mockIpc';\n\nexport interface ViewportPreset {\n name: string;\n width: number;\n height: number;\n}\n\nexport const VIEWPORT_PRESETS = {\n /** Minimum supported window size (e.g., ) */\n MIN_SUPPORTED: { name: 'min-supported', width: 1024, height: 768 } as ViewportPreset,\n /** Standard default window size (e.g., ) */\n DEFAULT: { name: 'default', width: 1200, height: 800 } as ViewportPreset,\n /** Wide screen for checking grids and tables */\n WIDE: { name: 'wide', width: 1600, height: 900 } as ViewportPreset,\n /** Full HD */\n FULL_HD: { name: 'full-hd', width: 1920, height: 1080 } as ViewportPreset,\n};\n\nexport interface CaptureOptions {\n selector?: string;\n fullPage?: boolean;\n mask?: string[];\n}\n\nexport interface CaptureBurstOptions {\n /** Total duration of burst animation capture in milliseconds */\n durationMs: number;\n /** Interval between frames in milliseconds (default: 80ms) */\n intervalMs?: number;\n /** Restrict capture to a specific element */\n selector?: string;\n}\n\nexport interface SnapshotMetadata {\n index: number;\n name: string;\n fileName: string;\n filePath: string;\n relativeUri: string;\n viewport: { width: number; height: number };\n timestamp: string;\n isBurstFrame?: boolean;\n burstGroup?: string;\n frameIndex?: number;\n selector?: string;\n}\n\nexport interface TestContext {\n page: Page;\n context: BrowserContext;\n targetMode: 'desktop' | 'preview';\n currentViewport: { width: number; height: number };\n \n // --- Snapshots ---\n\n /** Capture a single high-quality snapshot of the window or element */\n capture: (name: string, options?: CaptureOptions) => Promise<SnapshotMetadata>;\n \n /** Capture a series of frames for animations (Framer Motion, modals, lists) */\n captureBurst: (name: string, options: CaptureBurstOptions) => Promise<SnapshotMetadata[]>;\n \n // --- Navigation ---\n\n /** \n * Dynamically navigate to another page during the test.\n * Example: await ctx.navigate('/settings');\n */\n navigate: (route: string) => Promise<void>;\n\n // --- Viewport ---\n\n /** Change window / viewport size */\n resize: (width: number, height: number) => Promise<void>;\n \n /** Set one of the standard presets (MIN_SUPPORTED, DEFAULT, WIDE) */\n setPreset: (preset: ViewportPreset) => Promise<void>;\n\n /** \n * Dynamically resize the window so it perfectly fits the specified element (or body).\n * Useful when an agent wants to test an isolated component without background clutter.\n */\n resizeToFit: (selector?: string, padding?: number) => Promise<void>;\n \n // --- Waiting ---\n\n /** Wait the specified amount of milliseconds */\n wait: (ms: number) => Promise<void>;\n \n /** Wait for the selector to appear in the DOM */\n waitForSelector: (selector: string, timeoutMs?: number) => Promise<void>;\n \n // --- Interaction ---\n\n /** Left-click the element */\n click: (selector: string) => Promise<void>;\n \n /** Right-click the element (Context Menu) */\n rightClick: (selector: string) => Promise<void>;\n \n /** Type text into an input field */\n type: (selector: string, text: string) => Promise<void>;\n \n /** Select an option in a <select> by its value */\n selectOption: (selector: string, value: string) => Promise<void>;\n \n /** Hover the cursor to check highlights / tooltips */\n hover: (selector: string) => Promise<void>;\n \n /** Scroll the element (or window) down by the specified number of pixels */\n scroll: (selector: string, deltaY: number) => Promise<void>;\n \n // --- Logging ---\n\n /** Log a step for the agent */\n log: (message: string) => void;\n\n // --- Mock IPC (preview mode only) ---\n\n /**\n * Set a mock response for an IPC action.\n * Changes the data returned by window.external.sendMessage in preview mode.\n * Ignored in desktop mode (IPC goes through real backend).\n *\n * Example:\n * await ctx.setMockIpc('GET_INSTANCES', [{ id: '...', name: 'Test' }]);\n * await ctx.setMockIpc('CREATE_INSTANCE', 'Some error', { type: 'ERROR' });\n */\n setMockIpc: (action: string, data: any, options?: { type?: MockIpcResponseType; delayMs?: number }) => Promise<void>;\n\n // --- Console Errors ---\n\n /** Return all intercepted console.error and pageerror logs */\n getConsoleErrors: () => ConsoleEntry[];\n\n /** Return all intercepted console.warn logs */\n getConsoleWarnings: () => ConsoleEntry[];\n\n /** Returns true if there are critical errors in the console */\n hasConsoleErrors: () => boolean;\n\n // --- DOM Assertions ---\n\n /** Get the visible text of a specific element */\n readText: (selector: string) => Promise<string | null>;\n \n /** Get ALL visible text on the page (useful for quick analysis without Vision) */\n getPageText: () => Promise<string>;\n \n /** Check if the element is visible on the screen */\n isVisible: (selector: string) => Promise<boolean>;\n\n /** Count the number of elements matching the selector */\n getElementCount: (selector: string) => Promise<number>;\n}\n\nexport interface VisualScenario {\n /** Unique scenario identifier (kebab-case) */\n id: string;\n /** Human-readable title for the report */\n title: string;\n /** Description of what is being tested */\n description?: string;\n /** Initial route to navigate to (e.g., '/instances', '/settings') */\n route?: string;\n /** List of viewport sizes to test */\n viewports?: ViewportPreset[];\n /**\n * Initial IPC mock data for preview mode.\n * Applied BEFORE navigation to the route.\n */\n mockIpc?: Array<{ action: string; data: any; type?: MockIpcResponseType; delayMs?: number }>;\n /**\n * Optional setup hook: executed before the browser navigates and scenario runs.\n * Ideal for preparing mock files, test directories, or initial state.\n */\n setup?: () => Promise<void> | void;\n /** The main body of the scenario */\n run: (ctx: TestContext) => Promise<void>;\n /**\n * Optional teardown hook: guaranteed to execute in finally, even if run() fails.\n * Ideal for cleaning up test artifacts, cache directories, or temporary state.\n */\n teardown?: () => Promise<void> | void;\n}\n\nexport function defineVisualTest(scenario: VisualScenario): VisualScenario {\n return scenario;\n}\n"],"mappings":";AAUO,IAAM,mBAAmB;AAAA;AAAA,EAE9B,eAAe,EAAE,MAAM,iBAAiB,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAEjE,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAErD,MAAM,EAAE,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAE/C,SAAS,EAAE,MAAM,WAAW,OAAO,MAAM,QAAQ,KAAK;AACxD;AAyKO,SAAS,iBAAiB,UAA0C;AACzE,SAAO;AACT;","names":[]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@_deep4wee/agent-lens",
3
+ "version": "1.0.1",
4
+ "description": "Visual self-check UI runner for AI coding agents",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "agent-lens": "dist/cli.js"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "skills",
16
+ "scripts",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "clean": "node -e \"const fs = require('fs'); ['dist', 'artifacts'].forEach(d => fs.rmSync(d, {recursive: true, force: true})); const files = fs.readdirSync('.'); files.filter(f => f.endsWith('.tgz')).forEach(f => fs.unlinkSync(f));\"",
22
+ "build": "npm run clean && tsup",
23
+ "build:pack": "npm run build && npm pack",
24
+ "postinstall": "node scripts/postinstall.js"
25
+ },
26
+ "peerDependencies": {
27
+ "playwright": "^1.40.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.0.0",
31
+ "playwright": "^1.63.0",
32
+ "tsup": "^8.0.2",
33
+ "typescript": "^5.0.0"
34
+ },
35
+ "dependencies": {
36
+ "jiti": "^2.7.0",
37
+ "tree-kill": "^1.2.2"
38
+ },
39
+ "author": "deep4wee",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/deep4wee/agent-lens.git"
44
+ },
45
+ "keywords": [
46
+ "ai",
47
+ "agent",
48
+ "visual",
49
+ "testing",
50
+ "playwright",
51
+ "automation",
52
+ "screenshot",
53
+ "ui"
54
+ ],
55
+ "type": "commonjs",
56
+ "bugs": {
57
+ "url": "https://github.com/deep4wee/agent-lens/issues"
58
+ },
59
+ "homepage": "https://github.com/deep4wee/agent-lens#readme"
60
+ }
@@ -0,0 +1,47 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ // When installed via npm, INIT_CWD is the root of the project installing this package.
5
+ // If not available, fallback to process.cwd() (though usually that's the package dir itself).
6
+ const targetProjectRoot = process.env.INIT_CWD || process.cwd();
7
+
8
+ const skillSourceDir = path.join(__dirname, '../skills/agent-lens');
9
+ const targetSkillDir = path.join(targetProjectRoot, '.agents', 'skills', 'agent-lens');
10
+
11
+ function copySkillRecursive(src, dest) {
12
+ if (!fs.existsSync(src)) return;
13
+
14
+ if (!fs.existsSync(dest)) {
15
+ fs.mkdirSync(dest, { recursive: true });
16
+ }
17
+
18
+ const entries = fs.readdirSync(src, { withFileTypes: true });
19
+ for (const entry of entries) {
20
+ const srcPath = path.join(src, entry.name);
21
+ const destPath = path.join(dest, entry.name);
22
+
23
+ if (entry.isDirectory()) {
24
+ copySkillRecursive(srcPath, destPath);
25
+ } else {
26
+ fs.copyFileSync(srcPath, destPath);
27
+ }
28
+ }
29
+ }
30
+
31
+ function copySkill() {
32
+ try {
33
+ if (!fs.existsSync(skillSourceDir)) {
34
+ return;
35
+ }
36
+
37
+ copySkillRecursive(skillSourceDir, targetSkillDir);
38
+ console.log(`[AgentLens] Successfully equipped AI skill at ${targetSkillDir}`);
39
+ } catch (error) {
40
+ console.warn('[AgentLens] Warning: Failed to copy SKILL.md automatically:', error.message);
41
+ }
42
+ }
43
+
44
+ // Don't run postinstall if we are developing inside the agent-lens repo itself
45
+ if (path.resolve(targetProjectRoot) !== path.resolve(path.join(__dirname, '..'))) {
46
+ copySkill();
47
+ }
@@ -0,0 +1,200 @@
1
+ ---
2
+ name: agent-lens
3
+ description: Visual UI self-verification tool for AI agents. Take responsive multi-viewport snapshots of live URLs or run scripted interaction scenarios in Chromium or native desktop WebView2. Catches visual regressions, layout shifts, silent console errors, and runtime exceptions.
4
+ ---
5
+
6
+ # 👁️ AgentLens
7
+
8
+ **AgentLens** is a visual self-verification tool designed specifically for autonomous AI coding agents.
9
+
10
+ Instead of writing frontend code and blindly guessing if it looks right, AgentLens gives you **eyes**. You can capture multi-viewport screenshots of your running app, focus on isolated components, test animations, intercept silent JavaScript errors, and inspect the resulting markdown report to iterate autonomously before showing the final result to the user.
11
+
12
+ ---
13
+
14
+ ## ⚡ Two Modes of Agent Verification
15
+
16
+ ### 1. Instant Verification (`snap`) — No test files needed!
17
+ Ideal for 90% of tasks when you just modified a page, component, or layout:
18
+ ```bash
19
+ # Verify a live dev server across desktop & mobile viewports:
20
+ npx agent-lens snap --url=http://localhost:5173
21
+
22
+ # Auto-start dev server in a monorepo (e.g. Frontend/), snap, and auto-terminate:
23
+ npx agent-lens snap --start="npm run dev" --start-cwd=./Frontend
24
+
25
+ # Zero-config smart snap (auto-detects active server or static build):
26
+ npx agent-lens snap
27
+
28
+ # Focus strictly on one component:
29
+ npx agent-lens snap --url=http://localhost:5173/settings --selector=".pricing-card"
30
+ ```
31
+
32
+ ### 2. Scripted Scenarios (`npx agent-lens --scenario=<name>`)
33
+ For multi-step flows, form submissions, state mocks, and complex assertions:
34
+ ```bash
35
+ npx agent-lens --scenario=checkout-flow --url=http://localhost:5173
36
+ ```
37
+
38
+ ---
39
+
40
+ ## 🧭 The Agent Workflow
41
+
42
+ 1. **Write or Edit the Code**: Implement the requested UI changes or components.
43
+ 2. **Choose Your Verification Method**:
44
+ - **Quick check**: Run `npx agent-lens snap`.
45
+ - **Interactive check**: Write a scenario file in `scenarios/<name>.scenario.ts`.
46
+ 3. **Execute the Runner**:
47
+ ```bash
48
+ npx agent-lens snap --url=http://localhost:5173
49
+ ```
50
+ 4. **Inspect the Output**:
51
+ - AgentLens always writes the latest report to:
52
+ `artifacts/latest/report.md`
53
+ - Use `view_file` to read `artifacts/latest/report.md` immediately without worrying about timestamped folder names.
54
+ - Check the **Console Errors** section — if errors exist, fix them!
55
+ 5. **Self-Correct & Iterate**: Re-run verification until the layout is solid and the console is clean.
56
+
57
+ ---
58
+
59
+ ## ⚠️ Agent Best Practices & Pitfalls to Avoid
60
+
61
+ ### 1. Avoid Localized Text in Selectors (Localization Pitfall)
62
+ ❌ **Never write:**
63
+ ```typescript
64
+ await ctx.type('input[placeholder*="Search"]', 'text'); // Fails if language is Ukrainian "Пошук"!
65
+ await ctx.click('button:has-text("Save")'); // Fails on non-English UI!
66
+ ```
67
+ ✅ **Always use semantic attributes, test IDs, or structural CSS:**
68
+ ```typescript
69
+ await ctx.type('input[type="search"]', 'text');
70
+ await ctx.type('input[name="query"]', 'text');
71
+ await ctx.click('button[type="submit"]');
72
+ await ctx.click('[data-testid="save-button"]');
73
+ ```
74
+
75
+ ### 2. Base Mocks in `scenarios/mocks.ts` (Prevent Root Crashes)
76
+ When your app mounts, the root component (Navbar, AuthContext, Layout) often queries base endpoints (e.g. `GET_USER`, `GET_SETTINGS`, `GET_ACCOUNTS`). If your scenario only mocks one sub-action, unmocked root actions may return empty and crash the app with `Cannot read properties of null (reading 'length')`.
77
+ - Place common base mocks in `scenarios/mocks.ts`.
78
+ - AgentLens automatically merges `scenarios/mocks.ts` with your scenario's specific `mockIpc: [...]` overrides!
79
+
80
+ ### 3. Monorepos & Subdirectories (`--start-cwd`)
81
+ If your frontend lives in a subfolder like `Frontend/` or `client/`:
82
+ - Pass `--start-cwd=./Frontend` (or configure `"startCwd": "./Frontend"` in `agent-lens.json`).
83
+ - AgentLens automatically auto-detects `Frontend/`, `client/`, or `web/` if root `package.json` lacks a `dev` script.
84
+
85
+ ### 4. Preventing Artifact Folder Bloat
86
+ To keep the workspace tidy across multiple test iterations, use:
87
+ ```bash
88
+ npx agent-lens snap --clean-artifacts
89
+ ```
90
+ Or always inspect the single persistent pointer:
91
+ `artifacts/latest/report.md`
92
+
93
+ ---
94
+
95
+ ## 💻 CLI Flags & Options
96
+
97
+ ```bash
98
+ npx agent-lens [command] [options]
99
+ ```
100
+
101
+ ### Commands:
102
+ - `snap`: Instant one-shot snapshot & console check of a URL or static build.
103
+ - `init`: Generate starter template in `scenarios/template.scenario.ts` and `scenarios/mocks.ts`.
104
+ - *(default)*: Runs matching scenarios from `scenarios/`.
105
+
106
+ ### Options:
107
+ | Flag | Description | Default |
108
+ | :--- | :--- | :--- |
109
+ | `--url=<url>` | Target URL to test (e.g. `http://localhost:5173`) | *Auto-detected* |
110
+ | `--start="<cmd>"` | Auto-launch dev server / backend before test | *(none)* |
111
+ | `--start-cwd=<path>` | Directory to run `--start` in (e.g. `--start-cwd=./Frontend`) | *Auto-detected* |
112
+ | `--clean-artifacts` | Purge older test runs in `artifacts/` | `false` |
113
+ | `--selector=<css>` | Focus and resize-to-fit a specific component | *(none)* |
114
+ | `--viewports=<list>` | Viewports to capture (`desktop,mobile,tablet` or `1200x800`) | `desktop,mobile` |
115
+ | `--wait=<ms>` | Wait time after page load before taking snapshots | `1000` |
116
+ | `--scenario=<id>` | Name or prefix of scenario file to run | *(none)* |
117
+ | `--all` | Run all discovered scenarios | `false` |
118
+ | `--mode=preview\|desktop` | Engine: `preview` (Web / Live URL) or `desktop` (native .exe) | `preview` |
119
+ | `--exe=<path>` | Path to compiled desktop executable for desktop mode | *(none)* |
120
+ | `--port=<port>` | CDP remote debugging port for desktop mode | `9222` |
121
+ | `--build[=<cmd>]` | Build command to run before testing | `npm run build` |
122
+ | `--clean=<paths>` | Comma-separated paths to purge on exit | *(none)* |
123
+ | `--folder=<path>` | Folder to store report & screenshots (also `--outDir`) | `artifacts` |
124
+ | `--headed` | Open visible browser window | `false` |
125
+ | `--detach` | Do not close browser or app on finish | `false` |
126
+
127
+ ---
128
+
129
+ ## 🛠️ DSL API Reference (`scenarios/*.scenario.ts`)
130
+
131
+ ```typescript
132
+ import { defineVisualTest, VIEWPORT_PRESETS, type TestContext } from 'agent-lens';
133
+ ```
134
+
135
+ ### Scenario Definition Structure:
136
+ ```typescript
137
+ export default defineVisualTest({
138
+ id: 'my-feature-check',
139
+ title: 'Feature Verification',
140
+ route: '/dashboard', // Route or URL
141
+ viewports: [VIEWPORT_PRESETS.DEFAULT, VIEWPORT_PRESETS.MIN_SUPPORTED],
142
+
143
+ // Lifecycle Setup: prepare temporary state or mock folders
144
+ setup: async () => {},
145
+
146
+ // Test Body: interact and capture
147
+ run: async (ctx) => {},
148
+
149
+ // Lifecycle Teardown: ALWAYS executes, guaranteed cleanup
150
+ teardown: async () => {}
151
+ });
152
+ ```
153
+
154
+ ### Available `ctx` Methods:
155
+
156
+ #### 📸 Capturing
157
+ - `await ctx.capture('01_name', options?)`: Capture full page or element snapshot.
158
+ - `await ctx.captureBurst('02_anim', { durationMs: 300, intervalMs: 50, selector? })`: Capture animation frames.
159
+
160
+ #### 📐 Viewport Control
161
+ - `await ctx.setPreset(VIEWPORT_PRESETS.DEFAULT)`: Standard desktop (1200x800).
162
+ - `await ctx.setPreset(VIEWPORT_PRESETS.MIN_SUPPORTED)`: Compact viewport (1024x768).
163
+ - `await ctx.setPreset(VIEWPORT_PRESETS.WIDE)`: Widescreen (1600x900).
164
+ - `await ctx.resize(width, height)`: Custom dimensions.
165
+ - `await ctx.resizeToFit('selector', padding?)`: Dynamically resize viewport to wrap an element tightly.
166
+
167
+ #### 🖱️ Interactions
168
+ - `await ctx.click('selector')` / `await ctx.rightClick('selector')`
169
+ - `await ctx.type('selector', 'text')`
170
+ - `await ctx.selectOption('selector', 'value')`
171
+ - `await ctx.hover('selector')`
172
+ - `await ctx.scroll('selector', deltaY)`
173
+
174
+ #### 🕒 Waiting & Assertions
175
+ - `await ctx.wait(ms)`
176
+ - `await ctx.waitForSelector('selector', timeoutMs?)`
177
+ - `const text = await ctx.readText('selector')`
178
+ - `const pageText = await ctx.getPageText()`
179
+ - `const isVisible = await ctx.isVisible('selector')`
180
+ - `const count = await ctx.getElementCount('selector')`
181
+
182
+ #### 🐛 Console & Logs
183
+ - `ctx.log('Message')`: Writes custom log entry into the final markdown report.
184
+ - `ctx.getConsoleErrors()`: Array of caught errors with stack traces.
185
+ - `ctx.getConsoleWarnings()`: Array of caught warnings.
186
+
187
+ #### 🎭 Dynamic Mock IPC (Web / Preview mode)
188
+ - `await ctx.setMockIpc('ACTION_NAME', payload, { type: 'SUCCESS' | 'ERROR', delayMs?: number })`: Dynamically alters mock data during test execution.
189
+
190
+ ---
191
+
192
+ ## 📚 Detailed Examples & Use Cases
193
+
194
+ Check the dedicated example guides in `examples/` for complete walk-throughs:
195
+ 1. [Instant Verification (`snap`)](examples/01-instant-verification-snap.md) — One-shot snapshotting without writing test files.
196
+ 2. [Live Dev Server Workflow](examples/02-dev-server-live-testing.md) — Testing active Vite/Next.js servers with `--start` or `--url`.
197
+ 3. [Component Isolation & Burst Animations](examples/03-component-isolation-and-animations.md) — Inspecting isolated components and CSS transitions.
198
+ 4. [Native Desktop App Testing](examples/04-desktop-native-testing.md) — Testing compiled `.exe` binaries with CDP and crash diagnostics.
199
+ 5. [Clean Teardown & Sandboxing](examples/05-clean-teardown-and-sandboxing.md) — Guaranteeing zero leftover test data using `teardown()` and `--clean`.
200
+ 6. [State Testing with Mock IPC](examples/06-state-testing-with-mock-ipc.md) — Testing empty states, errors, and data tables.
@@ -0,0 +1,53 @@
1
+ # Example 1: Instant Visual Self-Check (`snap`)
2
+
3
+ The `snap` command is the fastest way for an AI agent to "see" its work immediately without writing any scenario files.
4
+
5
+ ## Use Cases
6
+ - After generating or editing a component, web page, or modal.
7
+ - Quickly verifying responsiveness across desktop and mobile screens.
8
+ - Checking for silent console errors and runtime exceptions.
9
+
10
+ ## Basic Usage
11
+
12
+ ```bash
13
+ # Check an already-running dev server
14
+ npx agent-lens snap --url=http://localhost:5173
15
+ ```
16
+
17
+ ## Options & Combinations
18
+
19
+ ### 1. Test Multiple Viewports (Desktop, Tablet, Mobile)
20
+ ```bash
21
+ npx agent-lens snap \
22
+ --url=http://localhost:3000/dashboard \
23
+ --viewports=desktop,tablet,mobile
24
+ ```
25
+
26
+ ### 2. Auto-Start Dev Server
27
+ If your dev server is not running yet, AgentLens can start it automatically, wait until it's ready, take the snapshots, and cleanly terminate the server when finished:
28
+ ```bash
29
+ npx agent-lens snap \
30
+ --start="npm run dev" \
31
+ --url=http://localhost:5173 \
32
+ --wait=1500
33
+ ```
34
+
35
+ ### 3. Focus on a Specific Component (`--selector`)
36
+ Take responsive snapshots of the entire page AND an isolated, fitted snapshot of a single component:
37
+ ```bash
38
+ npx agent-lens snap \
39
+ --url=http://localhost:5173/settings \
40
+ --selector=".pricing-card" \
41
+ --name="pricing_component"
42
+ ```
43
+
44
+ ### 4. Custom Output Directory
45
+ Save visual reports directly to a project folder (make sure to add it to `.gitignore`):
46
+ ```bash
47
+ npx agent-lens snap \
48
+ --url=http://localhost:5173 \
49
+ --folder=visual-reports
50
+ ```
51
+
52
+ ## Reading the Result
53
+ AgentLens outputs the absolute path to `report.md`. Inspect the markdown report using your file reading tool to review the images and console logs.