@empiricalrun/test-gen 0.61.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/agent/browsing/run.d.ts +2 -0
- package/dist/agent/browsing/run.d.ts.map +1 -1
- package/dist/agent/browsing/run.js +11 -8
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +1 -1
- package/dist/agent/chat/agent-loop.js +2 -3
- package/dist/agent/chat/exports.d.ts +2 -2
- package/dist/agent/chat/exports.d.ts.map +1 -1
- package/dist/agent/chat/exports.js +1 -1
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +24 -4
- package/dist/agent/chat/models.d.ts +1 -3
- package/dist/agent/chat/models.d.ts.map +1 -1
- package/dist/agent/chat/models.js +4 -25
- package/dist/agent/cua/computer.d.ts +6 -6
- package/dist/agent/cua/computer.d.ts.map +1 -1
- package/dist/agent/cua/computer.js +38 -83
- package/dist/agent/cua/index.d.ts +2 -1
- package/dist/agent/cua/index.d.ts.map +1 -1
- package/dist/agent/cua/index.js +26 -33
- package/dist/agent/cua/pw-codegen/element-from-point.d.ts +8 -0
- package/dist/agent/cua/pw-codegen/element-from-point.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/element-from-point.js +118 -0
- package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts +15 -0
- package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/pw-pause/index.js +84 -0
- package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts +16 -0
- package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/pw-pause/utils.js +98 -0
- package/dist/agent/cua/pw-codegen/types.d.ts +46 -0
- package/dist/agent/cua/pw-codegen/types.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/types.js +2 -0
- package/dist/agent/master/browser-tests/cua.spec.js +13 -1
- package/dist/artifacts/index.d.ts +52 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +237 -0
- package/dist/bin/index.js +7 -11
- package/dist/bin/utils/index.d.ts +5 -3
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/bin/utils/index.js +13 -0
- package/dist/bin/utils/platform/web/index.d.ts +1 -1
- package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
- package/dist/bin/utils/platform/web/index.js +3 -2
- package/dist/bin/utils/scenarios/index.d.ts +3 -3
- package/dist/file/client.d.ts +2 -0
- package/dist/file/client.d.ts.map +1 -1
- package/dist/file/client.js +16 -0
- package/dist/file/server.d.ts +3 -1
- package/dist/file/server.d.ts.map +1 -1
- package/dist/file/server.js +27 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/test-build/index.d.ts +6 -2
- package/dist/test-build/index.d.ts.map +1 -1
- package/dist/test-build/index.js +9 -7
- package/dist/tool-call-service/index.d.ts +14 -7
- package/dist/tool-call-service/index.d.ts.map +1 -1
- package/dist/tool-call-service/index.js +36 -10
- package/dist/tools/commit-and-create-pr.d.ts.map +1 -1
- package/dist/tools/commit-and-create-pr.js +11 -4
- package/dist/tools/diagnosis-fetcher.d.ts.map +1 -1
- package/dist/tools/diagnosis-fetcher.js +4 -3
- package/dist/tools/download-build.d.ts.map +1 -1
- package/dist/tools/download-build.js +3 -3
- package/dist/tools/environment-crud.d.ts.map +1 -1
- package/dist/tools/environment-crud.js +6 -4
- package/dist/tools/grep/index.d.ts.map +1 -1
- package/dist/tools/grep/index.js +13 -11
- package/dist/tools/str_replace_editor.d.ts +1 -1
- package/dist/tools/str_replace_editor.d.ts.map +1 -1
- package/dist/tools/str_replace_editor.js +38 -28
- package/dist/tools/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/test-gen-browser.js +18 -4
- package/dist/tools/test-run-fetcher/index.d.ts.map +1 -1
- package/dist/tools/test-run-fetcher/index.js +2 -1
- package/dist/tools/test-run.d.ts.map +1 -1
- package/dist/tools/test-run.js +10 -8
- package/dist/tools/utils/index.d.ts +17 -2
- package/dist/tools/utils/index.d.ts.map +1 -1
- package/dist/tools/utils/index.js +51 -7
- package/dist/utils/checkpoint.d.ts +5 -1
- package/dist/utils/checkpoint.d.ts.map +1 -1
- package/dist/utils/checkpoint.js +8 -3
- package/dist/utils/exec.d.ts +2 -0
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +4 -1
- package/dist/utils/git.d.ts +12 -7
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +27 -17
- package/dist/utils/slug.d.ts +16 -0
- package/dist/utils/slug.d.ts.map +1 -1
- package/dist/utils/slug.js +27 -1
- package/package.json +6 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/pw-test.d.ts +0 -2
- package/dist/utils/pw-test.d.ts.map +0 -1
- package/dist/utils/pw-test.js +0 -13
package/dist/agent/cua/index.js
CHANGED
|
@@ -3,37 +3,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.startPlaywrightCodegen = startPlaywrightCodegen;
|
|
7
6
|
exports.createTestUsingComputerUseAgent = createTestUsingComputerUseAgent;
|
|
8
7
|
const llm_1 = require("@empiricalrun/llm");
|
|
9
8
|
const openai_1 = __importDefault(require("openai"));
|
|
10
|
-
const utils_1 = require("../browsing/utils");
|
|
11
9
|
const computer_1 = require("./computer");
|
|
12
10
|
const model_1 = require("./model");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const glassPane = document.querySelector("x-pw-glass");
|
|
27
|
-
if (glassPane) {
|
|
28
|
-
glassPane.remove();
|
|
29
|
-
}
|
|
30
|
-
}, 3000);
|
|
31
|
-
});
|
|
32
|
-
await page.pause();
|
|
11
|
+
const element_from_point_1 = require("./pw-codegen/element-from-point");
|
|
12
|
+
function getCodegen() {
|
|
13
|
+
return new element_from_point_1.ElementFromPointCodegen();
|
|
14
|
+
// TODO: Add support for page.pause approach
|
|
15
|
+
// We can use PlaywrightPauseCodegen if playwright patch was successful,
|
|
16
|
+
// IPC port is available and PW_CODEGEN_NO_INSPECTOR env var is set
|
|
17
|
+
}
|
|
18
|
+
function getStructuredArtifactInput(screenshotBytes, actionName) {
|
|
19
|
+
return {
|
|
20
|
+
name: `${actionName}`,
|
|
21
|
+
contentType: "image/png",
|
|
22
|
+
data: Buffer.from(screenshotBytes, "base64"),
|
|
23
|
+
};
|
|
33
24
|
}
|
|
34
25
|
async function createTestUsingComputerUseAgent({ page, task, trace, }) {
|
|
35
|
-
|
|
26
|
+
const codegen = getCodegen();
|
|
27
|
+
await codegen.initialize(page);
|
|
36
28
|
const screenshotBytes = await (0, computer_1.getScreenshot)(page);
|
|
29
|
+
const artifacts = [
|
|
30
|
+
getStructuredArtifactInput(screenshotBytes, "Initial Screen"),
|
|
31
|
+
];
|
|
37
32
|
const viewport = page.viewportSize();
|
|
38
33
|
let screenWidth = viewport?.width || 1280;
|
|
39
34
|
let screenHeight = viewport?.height || 720;
|
|
@@ -101,7 +96,7 @@ async function createTestUsingComputerUseAgent({ page, task, trace, }) {
|
|
|
101
96
|
const functionCall = functionCalls[0];
|
|
102
97
|
if (functionCall) {
|
|
103
98
|
const args = JSON.parse(functionCall.arguments);
|
|
104
|
-
const { actionSummary, actionCode } = await (0, computer_1.
|
|
99
|
+
const { actionSummary, actionCode } = await (0, computer_1.executeModelAction)(page, { type: "goto", url: args.url }, codegen);
|
|
105
100
|
executedActionSummary = actionSummary;
|
|
106
101
|
actionsSummary.push(`Action executed: ${actionSummary}`);
|
|
107
102
|
if (actionCode) {
|
|
@@ -119,19 +114,15 @@ async function createTestUsingComputerUseAgent({ page, task, trace, }) {
|
|
|
119
114
|
const computerCall = computerCalls[0];
|
|
120
115
|
const action = computerCall.action;
|
|
121
116
|
// Execute the action and take a screenshot
|
|
122
|
-
const { actionSummary, actionCode } = await (0, computer_1.
|
|
117
|
+
const { actionSummary, actionCode } = await (0, computer_1.executeModelAction)(page, action, codegen);
|
|
123
118
|
executedActionSummary = actionSummary;
|
|
124
119
|
actionsSummary.push(`Action executed: ${actionSummary}`);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
generatedCode += actionCode;
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
actionsSummary.push(`No code generated: Will rely on Playwright's ability to auto-wait or auto-scroll`);
|
|
131
|
-
}
|
|
120
|
+
actionsSummary.push(`Generated code: ${actionCode}`);
|
|
121
|
+
generatedCode += actionCode;
|
|
132
122
|
// Allow time for changes to take effect.
|
|
133
123
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
134
124
|
const screenshotBytes = await (0, computer_1.getScreenshot)(page);
|
|
125
|
+
artifacts.push(getStructuredArtifactInput(screenshotBytes, actionSummary));
|
|
135
126
|
// Populate toolCallOutput
|
|
136
127
|
toolCallOutput = {
|
|
137
128
|
type: "computer_call_output",
|
|
@@ -174,7 +165,9 @@ async function createTestUsingComputerUseAgent({ page, task, trace, }) {
|
|
|
174
165
|
return {
|
|
175
166
|
actionsSummary: actionsSummary.join("\n"),
|
|
176
167
|
code: generatedCode,
|
|
177
|
-
// TODO: Does not support skills
|
|
168
|
+
// TODO: Does not support skills (from helper methods in pages/ dir),
|
|
169
|
+
// and therefore, import paths are empty
|
|
178
170
|
importPaths: [],
|
|
171
|
+
artifacts,
|
|
179
172
|
};
|
|
180
173
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Page } from "playwright";
|
|
2
|
+
import { Action, BasePlaywrightCodegen } from "./types";
|
|
3
|
+
export declare class ElementFromPointCodegen implements BasePlaywrightCodegen {
|
|
4
|
+
private page;
|
|
5
|
+
initialize(page: Page): Promise<void>;
|
|
6
|
+
codeForAction(action: Action): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=element-from-point.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-from-point.d.ts","sourceRoot":"","sources":["../../../../src/agent/cua/pw-codegen/element-from-point.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AA4DxD,qBAAa,uBAAwB,YAAW,qBAAqB;IACnE,OAAO,CAAC,IAAI,CAAmB;IACzB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA0DrD"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ElementFromPointCodegen = void 0;
|
|
4
|
+
const utils_1 = require("../../browsing/utils");
|
|
5
|
+
async function getLocatorForClick(page, { x, y }) {
|
|
6
|
+
const locator = await page.evaluate(([x, y]) => {
|
|
7
|
+
const element = document.elementFromPoint(x, y);
|
|
8
|
+
const bbox = element?.getBoundingClientRect();
|
|
9
|
+
return {
|
|
10
|
+
locator: window.playwright.generateLocator(element),
|
|
11
|
+
isIframe: element?.tagName === "IFRAME",
|
|
12
|
+
x0: bbox?.x,
|
|
13
|
+
y0: bbox?.y,
|
|
14
|
+
src: element?.getAttribute("src"),
|
|
15
|
+
id: element?.getAttribute("id"),
|
|
16
|
+
};
|
|
17
|
+
}, [x, y]);
|
|
18
|
+
if (!locator.isIframe) {
|
|
19
|
+
return locator.locator;
|
|
20
|
+
}
|
|
21
|
+
await (0, utils_1.injectPwLocatorGenerator)(page);
|
|
22
|
+
const relativeX = x - locator.x0;
|
|
23
|
+
const relativeY = y - locator.y0;
|
|
24
|
+
// TODO: Reuse locator.locator for the frameLocator.
|
|
25
|
+
let frameEl = locator.src
|
|
26
|
+
? page.locator(`[src="${locator.src}"]`)
|
|
27
|
+
: page.locator(`#${locator.id}`);
|
|
28
|
+
const frameLocator = locator.src
|
|
29
|
+
? `locator('[src="${locator.src}"]')`
|
|
30
|
+
: `locator('#${locator.id}')`;
|
|
31
|
+
const elementLocatorInsideFrame = await frameEl
|
|
32
|
+
.contentFrame()
|
|
33
|
+
.locator(":root")
|
|
34
|
+
.evaluate((rootElement, coords) => {
|
|
35
|
+
const xPos = coords[0];
|
|
36
|
+
const yPos = coords[1];
|
|
37
|
+
if (xPos === undefined || yPos === undefined) {
|
|
38
|
+
throw new Error("Coordinates are undefined in evaluate call");
|
|
39
|
+
}
|
|
40
|
+
const element = document.elementFromPoint(xPos, yPos);
|
|
41
|
+
return window.playwright.generateLocator(element);
|
|
42
|
+
}, [relativeX, relativeY]);
|
|
43
|
+
return `${frameLocator}.contentFrame().${elementLocatorInsideFrame}`;
|
|
44
|
+
}
|
|
45
|
+
async function getLocatorForFill(page) {
|
|
46
|
+
const locator = await page.evaluate(() => {
|
|
47
|
+
const element = document.activeElement;
|
|
48
|
+
return window.playwright.generateLocator(element);
|
|
49
|
+
});
|
|
50
|
+
return locator;
|
|
51
|
+
}
|
|
52
|
+
class ElementFromPointCodegen {
|
|
53
|
+
page;
|
|
54
|
+
async initialize(page) {
|
|
55
|
+
this.page = page;
|
|
56
|
+
await (0, utils_1.injectPwLocatorGenerator)(page);
|
|
57
|
+
}
|
|
58
|
+
async codeForAction(action) {
|
|
59
|
+
try {
|
|
60
|
+
if (action.type === "goto") {
|
|
61
|
+
return `await page.goto("${action.url}");\n`;
|
|
62
|
+
}
|
|
63
|
+
if (action.type === "click") {
|
|
64
|
+
const { x, y } = action;
|
|
65
|
+
if (!this.page) {
|
|
66
|
+
throw new Error("Page is not initialized");
|
|
67
|
+
}
|
|
68
|
+
const locator = await getLocatorForClick(this.page, { x, y });
|
|
69
|
+
return `await page.${locator}.click();\n`;
|
|
70
|
+
}
|
|
71
|
+
if (action.type === "type") {
|
|
72
|
+
if (!this.page) {
|
|
73
|
+
throw new Error("Page is not initialized");
|
|
74
|
+
}
|
|
75
|
+
const locator = await getLocatorForFill(this.page);
|
|
76
|
+
return `await page.${locator}.fill("${action.text}");\n`;
|
|
77
|
+
}
|
|
78
|
+
if (action.type === "back") {
|
|
79
|
+
return `await page.goBack();\n`;
|
|
80
|
+
}
|
|
81
|
+
if (action.type === "forward") {
|
|
82
|
+
return `await page.goForward();\n`;
|
|
83
|
+
}
|
|
84
|
+
if (action.type === "doubleclick") {
|
|
85
|
+
const { x, y } = action;
|
|
86
|
+
if (!this.page) {
|
|
87
|
+
throw new Error("Page is not initialized");
|
|
88
|
+
}
|
|
89
|
+
const locator = await getLocatorForClick(this.page, { x, y });
|
|
90
|
+
return `await page.${locator}.dblclick();\n`;
|
|
91
|
+
}
|
|
92
|
+
if (action.type === "move") {
|
|
93
|
+
const { x, y } = action;
|
|
94
|
+
return `await page.mouse.move(${x}, ${y});\n`;
|
|
95
|
+
}
|
|
96
|
+
if (action.type === "drag") {
|
|
97
|
+
// const { path } = action;
|
|
98
|
+
// TODO: This needs to be fixed
|
|
99
|
+
return `No code generated: drag action not supported by code generation`;
|
|
100
|
+
}
|
|
101
|
+
if (action.type === "scroll") {
|
|
102
|
+
return `No code to generate: will rely on Playwright's ability to auto-wait`;
|
|
103
|
+
}
|
|
104
|
+
if (action.type === "keypress") {
|
|
105
|
+
const { keys } = action;
|
|
106
|
+
return `await page.keyboard.press("${keys.join("+")}");\n`;
|
|
107
|
+
}
|
|
108
|
+
if (action.type === "wait") {
|
|
109
|
+
return `No code to generate: will rely on Playwright's ability to auto-wait`;
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`Unsupported action: ${JSON.stringify(action)}`);
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
return `Error generating code for action: ${e.message || e.toString()}`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.ElementFromPointCodegen = ElementFromPointCodegen;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Page } from "playwright";
|
|
2
|
+
export { revertToOriginalPwCode } from "./utils";
|
|
3
|
+
export declare const PW_PAUSE_IPC_PORT = 3039;
|
|
4
|
+
export declare function preparePlaywrightForCodegen(repoDir: string): Promise<void>;
|
|
5
|
+
export declare class PlaywrightPauseCodegen {
|
|
6
|
+
private port;
|
|
7
|
+
private page;
|
|
8
|
+
private server;
|
|
9
|
+
constructor();
|
|
10
|
+
initialize(page: Page): Promise<void>;
|
|
11
|
+
startPlaywrightCodegen(page: Page): Promise<void>;
|
|
12
|
+
recordAction(): Promise<void>;
|
|
13
|
+
getCodegenResult(): Promise<string>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AASvC,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEjD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,MAAM,iBAoBhE;AAGD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;;IAMpD,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;IAsBjC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;CAI1C"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PlaywrightPauseCodegen = exports.PW_PAUSE_IPC_PORT = exports.revertToOriginalPwCode = void 0;
|
|
7
|
+
exports.preparePlaywrightForCodegen = preparePlaywrightForCodegen;
|
|
8
|
+
const express_1 = __importDefault(require("express"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
var utils_2 = require("./utils");
|
|
13
|
+
Object.defineProperty(exports, "revertToOriginalPwCode", { enumerable: true, get: function () { return utils_2.revertToOriginalPwCode; } });
|
|
14
|
+
exports.PW_PAUSE_IPC_PORT = 3039;
|
|
15
|
+
async function preparePlaywrightForCodegen(repoDir) {
|
|
16
|
+
const npmListOutput = await (0, utils_1.runNpmList)(repoDir);
|
|
17
|
+
if (!npmListOutput) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const playwrightCorePath = (0, utils_1.getPlaywrightCoreFromNpmList)(npmListOutput);
|
|
21
|
+
const pathToRecorderApp = path_1.default.join(playwrightCorePath, "lib", "server", "recorder", "recorderApp.js");
|
|
22
|
+
if (!fs_1.default.existsSync(pathToRecorderApp)) {
|
|
23
|
+
const errMsg = `Cannot patch Playwright: ${pathToRecorderApp} does not exist`;
|
|
24
|
+
throw new Error(errMsg);
|
|
25
|
+
}
|
|
26
|
+
await (0, utils_1.createFileBackup)(pathToRecorderApp);
|
|
27
|
+
const port = exports.PW_PAUSE_IPC_PORT;
|
|
28
|
+
await (0, utils_1.patchPwCode)(pathToRecorderApp, port);
|
|
29
|
+
}
|
|
30
|
+
// TODO: Fix this to implement BasePlaywrightCodegen
|
|
31
|
+
class PlaywrightPauseCodegen {
|
|
32
|
+
port = 0;
|
|
33
|
+
page;
|
|
34
|
+
server;
|
|
35
|
+
constructor() {
|
|
36
|
+
this.port = exports.PW_PAUSE_IPC_PORT;
|
|
37
|
+
}
|
|
38
|
+
async initialize(page) {
|
|
39
|
+
// Start server to receive generated code from patch
|
|
40
|
+
const app = (0, express_1.default)();
|
|
41
|
+
app.use(express_1.default.json());
|
|
42
|
+
app.post("/test", async (req, res) => {
|
|
43
|
+
console.log("--- Received request in PlaywrightPatchCodegen ---");
|
|
44
|
+
console.log(req.body);
|
|
45
|
+
return res.send({ success: true });
|
|
46
|
+
});
|
|
47
|
+
await new Promise((resolve) => {
|
|
48
|
+
this.server = app.listen(this.port, () => resolve());
|
|
49
|
+
});
|
|
50
|
+
console.log(`Server started on port ${this.port}`);
|
|
51
|
+
// Start page.pause experience in the page
|
|
52
|
+
this.page = page;
|
|
53
|
+
await this.startPlaywrightCodegen(page);
|
|
54
|
+
}
|
|
55
|
+
async startPlaywrightCodegen(page) {
|
|
56
|
+
// TODO: Glass pane needs to be removed on every page load
|
|
57
|
+
// We use bindings that Playwright exposes to the page
|
|
58
|
+
// Ref: https://github.com/microsoft/playwright/blob/e1c8e0f6b33923c95cc4b9416aefa6977b1d3c55/packages/playwright-core/src/server/recorder.ts#L191
|
|
59
|
+
await page.evaluate(() => {
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
// First, we start recording
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
console.log(window["__pw_recorderSetMode"]("recording"));
|
|
64
|
+
// Then, we will resume the effect of pause()
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
console.log(window["__pw_resume"]());
|
|
67
|
+
// Then, we remove highlights that Playwright shows on the screen
|
|
68
|
+
const glassPane = document.querySelector("x-pw-glass");
|
|
69
|
+
if (glassPane) {
|
|
70
|
+
glassPane.remove();
|
|
71
|
+
}
|
|
72
|
+
}, 1000);
|
|
73
|
+
});
|
|
74
|
+
await page.pause();
|
|
75
|
+
}
|
|
76
|
+
async recordAction() {
|
|
77
|
+
console.log("Record action is no-op");
|
|
78
|
+
}
|
|
79
|
+
async getCodegenResult() {
|
|
80
|
+
console.log("Get codegen result is no-op");
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.PlaywrightPauseCodegen = PlaywrightPauseCodegen;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpful commands for debugging:
|
|
3
|
+
*
|
|
4
|
+
* 1. Find playwright-core path
|
|
5
|
+
* cd `npm ls playwright-core --json --long | jq -r '.dependencies["@playwright/test"].dependencies.playwright.dependencies["playwright-core"].path'`
|
|
6
|
+
*
|
|
7
|
+
* 2. Find the path to the recorder app
|
|
8
|
+
* cat lib/server/recorder/recorderApp.js
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export declare function runNpmList(repoDir: string): Promise<string | undefined>;
|
|
12
|
+
export declare function getPlaywrightCoreFromNpmList(output: string): any;
|
|
13
|
+
export declare function revertToOriginalPwCode(repoDir: string): Promise<void>;
|
|
14
|
+
export declare function createFileBackup(pathToFile: string): Promise<void>;
|
|
15
|
+
export declare function patchPwCode(pathToRecorderApp: string, port: number): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/utils.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,+BAiB/C;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,OAuB1D;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,iBAoB3D;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,iBAGxD;AAED,wBAAsB,WAAW,CAAC,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,iBAkBxE"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runNpmList = runNpmList;
|
|
7
|
+
exports.getPlaywrightCoreFromNpmList = getPlaywrightCoreFromNpmList;
|
|
8
|
+
exports.revertToOriginalPwCode = revertToOriginalPwCode;
|
|
9
|
+
exports.createFileBackup = createFileBackup;
|
|
10
|
+
exports.patchPwCode = patchPwCode;
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
/**
|
|
15
|
+
* Helpful commands for debugging:
|
|
16
|
+
*
|
|
17
|
+
* 1. Find playwright-core path
|
|
18
|
+
* cd `npm ls playwright-core --json --long | jq -r '.dependencies["@playwright/test"].dependencies.playwright.dependencies["playwright-core"].path'`
|
|
19
|
+
*
|
|
20
|
+
* 2. Find the path to the recorder app
|
|
21
|
+
* cat lib/server/recorder/recorderApp.js
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
async function runNpmList(repoDir) {
|
|
25
|
+
try {
|
|
26
|
+
const output = (0, child_process_1.execSync)("npm ls playwright-core --json --long --production=false", {
|
|
27
|
+
cwd: repoDir,
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
});
|
|
30
|
+
return output;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error("Command failed:", error.message);
|
|
34
|
+
console.error("Error code:", error.status);
|
|
35
|
+
console.error("Signal:", error.signal);
|
|
36
|
+
console.error("Stdout:", error.stdout?.toString());
|
|
37
|
+
console.error("Stderr:", error.stderr?.toString());
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function getPlaywrightCoreFromNpmList(output) {
|
|
41
|
+
const result = JSON.parse(output);
|
|
42
|
+
// We expect to find playwright-core as a dependency of playwright, which is
|
|
43
|
+
// a dependency of @playwright/test
|
|
44
|
+
const { dependencies } = result;
|
|
45
|
+
const playwrightTest = dependencies["@playwright/test"];
|
|
46
|
+
if (!playwrightTest) {
|
|
47
|
+
throw new Error("Could not find @playwright/test in npm dependencies");
|
|
48
|
+
}
|
|
49
|
+
const playwrightTestVersion = playwrightTest.version;
|
|
50
|
+
if (playwrightTestVersion !== "1.47.1") {
|
|
51
|
+
throw new Error("Unsupported playwright-test version: ${playwrightTestVersion}");
|
|
52
|
+
}
|
|
53
|
+
const playwrightDeps = playwrightTest.dependencies["playwright"];
|
|
54
|
+
const playwrightCoreDep = playwrightDeps.dependencies["playwright-core"];
|
|
55
|
+
if (!playwrightCoreDep) {
|
|
56
|
+
throw new Error("Could not find playwright-core in @playwright/test dependencies");
|
|
57
|
+
}
|
|
58
|
+
return playwrightCoreDep.path;
|
|
59
|
+
}
|
|
60
|
+
async function revertToOriginalPwCode(repoDir) {
|
|
61
|
+
try {
|
|
62
|
+
const npmListOutput = await runNpmList(repoDir);
|
|
63
|
+
if (!npmListOutput) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const playwrightCorePath = getPlaywrightCoreFromNpmList(npmListOutput);
|
|
67
|
+
const pathToRecorderApp = path_1.default.join(playwrightCorePath, "lib", "server", "recorder", "recorderApp.js");
|
|
68
|
+
const backupPath = `${pathToRecorderApp}.backup`;
|
|
69
|
+
fs_1.default.copyFileSync(backupPath, pathToRecorderApp);
|
|
70
|
+
fs_1.default.unlinkSync(backupPath);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error("Error reverting to original Playwright code", error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function createFileBackup(pathToFile) {
|
|
77
|
+
const backupPath = `${pathToFile}.backup`;
|
|
78
|
+
fs_1.default.copyFileSync(pathToFile, backupPath);
|
|
79
|
+
}
|
|
80
|
+
async function patchPwCode(pathToRecorderApp, port) {
|
|
81
|
+
const currentFileContents = fs_1.default.readFileSync(pathToRecorderApp, "utf-8");
|
|
82
|
+
const original = `async setSources(sources) {}`;
|
|
83
|
+
// TODO: Can we have a request payload and parse it in our code
|
|
84
|
+
const replacement = `async setSources(sources) {
|
|
85
|
+
const payload = JSON.stringify(sources.filter(source => source.isRecorded).filter(source => source.id === "javascript").map(source => source.actions));
|
|
86
|
+
const body = JSON.stringify({ payload });
|
|
87
|
+
await fetch("http://localhost:${port}/test", {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: {
|
|
90
|
+
"Content-Type": "application/json",
|
|
91
|
+
},
|
|
92
|
+
body,
|
|
93
|
+
});
|
|
94
|
+
}`;
|
|
95
|
+
const newCode = currentFileContents.replace(original, replacement);
|
|
96
|
+
fs_1.default.writeFileSync(pathToRecorderApp, newCode);
|
|
97
|
+
console.log(`Patched Playwright recorder app at ${pathToRecorderApp}`);
|
|
98
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Page } from "playwright";
|
|
2
|
+
export type Action = {
|
|
3
|
+
type: "click";
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
} | {
|
|
7
|
+
type: "doubleclick";
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
} | {
|
|
11
|
+
type: "back";
|
|
12
|
+
} | {
|
|
13
|
+
type: "forward";
|
|
14
|
+
} | {
|
|
15
|
+
type: "move";
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
} | {
|
|
19
|
+
type: "drag";
|
|
20
|
+
path: {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
}[];
|
|
24
|
+
} | {
|
|
25
|
+
type: "scroll";
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
scroll_x: number;
|
|
29
|
+
scroll_y: number;
|
|
30
|
+
} | {
|
|
31
|
+
type: "keypress";
|
|
32
|
+
keys: string[];
|
|
33
|
+
} | {
|
|
34
|
+
type: "type";
|
|
35
|
+
text: string;
|
|
36
|
+
} | {
|
|
37
|
+
type: "goto";
|
|
38
|
+
url: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "wait";
|
|
41
|
+
};
|
|
42
|
+
export interface BasePlaywrightCodegen {
|
|
43
|
+
initialize(page: Page): Promise<void>;
|
|
44
|
+
codeForAction(action: Action): Promise<string>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/agent/cua/pw-codegen/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,MAAM,MAAM,GACd;IACE,IAAI,EAAE,OAAO,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,GACD;IACE,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,IAAI,EAAE,SAAS,CAAC;CACjB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAClC,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb,GACD;IACE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEN,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const run_1 = require("../run");
|
|
4
4
|
const fixtures_1 = require("./fixtures");
|
|
5
|
-
|
|
5
|
+
fixtures_1.test.skip("computer use agent generates code for iframes", async ({ page, server, }) => {
|
|
6
6
|
await page.goto(`${server.baseURL}/iframe-elements.html`);
|
|
7
7
|
const result = await (0, run_1.createTestUsingComputerUseAgent)({
|
|
8
8
|
page,
|
|
@@ -11,3 +11,15 @@ const fixtures_1 = require("./fixtures");
|
|
|
11
11
|
(0, fixtures_1.expect)(result.code).toMatch(/page\.getByPlaceholder\(['"]Enter your name['"]?\)\.click/);
|
|
12
12
|
(0, fixtures_1.expect)(result.code).toMatch(/locator\(['"]#nested-frame['"]?\)\.contentFrame\(\)\.getByPlaceholder\(['"]Enter your name['"]?\)/);
|
|
13
13
|
});
|
|
14
|
+
fixtures_1.test.skip("computer use agent generates code for google login", async ({ page, }) => {
|
|
15
|
+
await page.goto(`https://dash.empirical.run`);
|
|
16
|
+
const result = await (0, run_1.createTestUsingComputerUseAgent)({
|
|
17
|
+
page,
|
|
18
|
+
task: `Click on the "Sign in with Google" button and enter dpdzero-test-user@empirical.run
|
|
19
|
+
as the email and click continue. then enter password as 1234556789`,
|
|
20
|
+
});
|
|
21
|
+
console.log(result.code);
|
|
22
|
+
(0, fixtures_1.expect)(result.code).toMatch(/page\.getByRole\('button', { name: 'Sign in with Google' }\)\.click/);
|
|
23
|
+
(0, fixtures_1.expect)(result.code).toContain("await page.getByLabel('Email or phone').fill(\"dpdzero-test-user@empirical.run\")");
|
|
24
|
+
(0, fixtures_1.expect)(result.code).toContain("await page.getByRole('button', { name: 'Next' }).click()");
|
|
25
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Artifact, ArtifactInput } from "@empiricalrun/shared-types";
|
|
2
|
+
export declare function isArtifactCollectionEnabled(): string | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Collects artifacts from the repository directory and returns their URLs.
|
|
5
|
+
*
|
|
6
|
+
* @param inputs - An array of artifact inputs.
|
|
7
|
+
* @param repoDir - The absolute path to the repository directory.
|
|
8
|
+
* @param toolCallId - Unique identifier for the tool call.
|
|
9
|
+
* @returns An array of artifacts with their URLs.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Collect artifacts from both file paths and in-memory data
|
|
13
|
+
* const artifacts = await collectArtifacts([
|
|
14
|
+
* {
|
|
15
|
+
* name: "screenshot.png",
|
|
16
|
+
* contentType: "image/png",
|
|
17
|
+
* path: "/Users/project/screenshots/test.png"
|
|
18
|
+
* },
|
|
19
|
+
* {
|
|
20
|
+
* name: "log.txt",
|
|
21
|
+
* contentType: "text/plain",
|
|
22
|
+
* data: Buffer.from("test log content")
|
|
23
|
+
* }
|
|
24
|
+
* ], "/Users/project", "tool-123");
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Returns empty array when artifact collection is disabled
|
|
28
|
+
* const artifacts = await collectArtifacts([], "/Users/project", "tool-123");
|
|
29
|
+
* // Returns: []
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Throws error for invalid file paths
|
|
33
|
+
* await collectArtifacts([
|
|
34
|
+
* {
|
|
35
|
+
* name: "test.png",
|
|
36
|
+
* contentType: "image/png",
|
|
37
|
+
* path: "relative/path/test.png" // Invalid - not absolute path
|
|
38
|
+
* }
|
|
39
|
+
* ], "/Users/project", "tool-123");
|
|
40
|
+
* // Throws: "Invalid path: relative/path/test.png..."
|
|
41
|
+
*/
|
|
42
|
+
export declare function collectArtifacts(inputs: ArtifactInput[], repoDir: string, toolCallId: string): Promise<Artifact[]>;
|
|
43
|
+
export declare class UploadArtifactsQueue {
|
|
44
|
+
private toolCallId;
|
|
45
|
+
private baseRepoPath;
|
|
46
|
+
private artifactResults;
|
|
47
|
+
private uploadPromise;
|
|
48
|
+
constructor(baseRepoPath: string, toolCallId: string);
|
|
49
|
+
addTask(artifacts: ArtifactInput[]): Promise<void>;
|
|
50
|
+
waitForCompletion(): Promise<Artifact[]>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/artifacts/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,QAAQ,EACR,aAAa,EAGd,MAAM,4BAA4B,CAAC;AAkBpC,wBAAgB,2BAA2B,uBAM1C;AA6HD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,EAAE,CAAC,CA6DrB;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAA8B;gBAEvC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAKvC,OAAO,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAelD,iBAAiB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;CAMtD"}
|