@empiricalrun/test-gen 0.60.0 → 0.62.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 (92) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/agent/browsing/run.d.ts +2 -0
  3. package/dist/agent/browsing/run.d.ts.map +1 -1
  4. package/dist/agent/browsing/run.js +11 -7
  5. package/dist/agent/browsing/utils.d.ts.map +1 -1
  6. package/dist/agent/browsing/utils.js +1 -1
  7. package/dist/agent/chat/agent-loop.js +2 -3
  8. package/dist/agent/chat/exports.d.ts +2 -2
  9. package/dist/agent/chat/exports.d.ts.map +1 -1
  10. package/dist/agent/chat/exports.js +1 -1
  11. package/dist/agent/chat/index.d.ts.map +1 -1
  12. package/dist/agent/chat/index.js +16 -2
  13. package/dist/agent/chat/models.d.ts +1 -3
  14. package/dist/agent/chat/models.d.ts.map +1 -1
  15. package/dist/agent/chat/models.js +4 -25
  16. package/dist/agent/chat/prompt.d.ts.map +1 -1
  17. package/dist/agent/chat/prompt.js +58 -0
  18. package/dist/agent/cua/computer.d.ts +6 -6
  19. package/dist/agent/cua/computer.d.ts.map +1 -1
  20. package/dist/agent/cua/computer.js +38 -83
  21. package/dist/agent/cua/index.d.ts +2 -1
  22. package/dist/agent/cua/index.d.ts.map +1 -1
  23. package/dist/agent/cua/index.js +26 -33
  24. package/dist/agent/cua/pw-codegen/element-from-point.d.ts +8 -0
  25. package/dist/agent/cua/pw-codegen/element-from-point.d.ts.map +1 -0
  26. package/dist/agent/cua/pw-codegen/element-from-point.js +118 -0
  27. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts +15 -0
  28. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -0
  29. package/dist/agent/cua/pw-codegen/pw-pause/index.js +84 -0
  30. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts +16 -0
  31. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts.map +1 -0
  32. package/dist/agent/cua/pw-codegen/pw-pause/utils.js +98 -0
  33. package/dist/agent/cua/pw-codegen/types.d.ts +46 -0
  34. package/dist/agent/cua/pw-codegen/types.d.ts.map +1 -0
  35. package/dist/agent/cua/pw-codegen/types.js +2 -0
  36. package/dist/agent/master/browser-tests/cua.spec.js +13 -1
  37. package/dist/artifacts/index.d.ts +43 -0
  38. package/dist/artifacts/index.d.ts.map +1 -0
  39. package/dist/artifacts/index.js +209 -0
  40. package/dist/bin/index.js +7 -11
  41. package/dist/bin/utils/index.d.ts +5 -3
  42. package/dist/bin/utils/index.d.ts.map +1 -1
  43. package/dist/bin/utils/index.js +13 -0
  44. package/dist/bin/utils/platform/web/index.d.ts +4 -1
  45. package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
  46. package/dist/bin/utils/platform/web/index.js +24 -8
  47. package/dist/bin/utils/scenarios/index.d.ts +3 -3
  48. package/dist/file/client.d.ts +2 -0
  49. package/dist/file/client.d.ts.map +1 -1
  50. package/dist/file/client.js +16 -0
  51. package/dist/file/server.d.ts +3 -1
  52. package/dist/file/server.d.ts.map +1 -1
  53. package/dist/file/server.js +27 -3
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -1
  56. package/dist/test-build/index.d.ts +6 -2
  57. package/dist/test-build/index.d.ts.map +1 -1
  58. package/dist/test-build/index.js +9 -7
  59. package/dist/tool-call-service/index.d.ts +14 -7
  60. package/dist/tool-call-service/index.d.ts.map +1 -1
  61. package/dist/tool-call-service/index.js +19 -8
  62. package/dist/tools/commit-and-create-pr.d.ts.map +1 -1
  63. package/dist/tools/commit-and-create-pr.js +5 -1
  64. package/dist/tools/download-build.d.ts.map +1 -1
  65. package/dist/tools/download-build.js +3 -3
  66. package/dist/tools/grep/index.d.ts.map +1 -1
  67. package/dist/tools/grep/index.js +4 -2
  68. package/dist/tools/str_replace_editor.d.ts.map +1 -1
  69. package/dist/tools/str_replace_editor.js +25 -8
  70. package/dist/tools/test-gen-browser.d.ts.map +1 -1
  71. package/dist/tools/test-gen-browser.js +21 -4
  72. package/dist/tools/test-run.d.ts.map +1 -1
  73. package/dist/tools/test-run.js +11 -8
  74. package/dist/tools/utils/index.d.ts +13 -0
  75. package/dist/tools/utils/index.d.ts.map +1 -1
  76. package/dist/tools/utils/index.js +47 -0
  77. package/dist/utils/exec.d.ts +2 -0
  78. package/dist/utils/exec.d.ts.map +1 -1
  79. package/dist/utils/exec.js +4 -1
  80. package/dist/utils/git.d.ts.map +1 -1
  81. package/dist/utils/git.js +1 -1
  82. package/dist/utils/slug.d.ts +16 -0
  83. package/dist/utils/slug.d.ts.map +1 -1
  84. package/dist/utils/slug.js +27 -1
  85. package/dist/utils/stripAnsi.d.ts +2 -0
  86. package/dist/utils/stripAnsi.d.ts.map +1 -0
  87. package/dist/utils/stripAnsi.js +9 -0
  88. package/package.json +6 -4
  89. package/tsconfig.tsbuildinfo +1 -1
  90. package/dist/utils/pw-test.d.ts +0 -2
  91. package/dist/utils/pw-test.d.ts.map +0 -1
  92. package/dist/utils/pw-test.js +0 -13
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAS/D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMlC,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,iBAoBtD;AAED,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,EACJ,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAqLD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAS3D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAwBlC,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,EACJ,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B,CAAC,CAyLD"}
@@ -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
- async function startPlaywrightCodegen(page) {
14
- // TODO: Use this method to offload code generation to Playwright
15
- // Unclear how to retrieve source code that is generated
16
- await page.evaluate(() => {
17
- setTimeout(() => {
18
- // First, we start recording
19
- // @ts-ignore
20
- console.log(window["__pw_recorderSetMode"]("recording"));
21
- // Then, we will resume the effect of pause()
22
- // @ts-ignore
23
- console.log(window["__pw_resume"]());
24
- // Then, we remove highlights that Playwright shows on the screen
25
- // @ts-ignore
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
- await (0, utils_1.injectPwLocatorGenerator)(page);
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.handlePageGoto)(page, args.url);
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.handleModelAction)(page, action);
117
+ const { actionSummary, actionCode } = await (0, computer_1.executeModelAction)(page, action, codegen);
123
118
  executedActionSummary = actionSummary;
124
119
  actionsSummary.push(`Action executed: ${actionSummary}`);
125
- if (actionCode) {
126
- actionsSummary.push(`Generated code: ${actionCode}`);
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, so import paths are empty
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"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
- (0, fixtures_1.test)("computer use agent generates code for iframes", async ({ page, server, }) => {
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,43 @@
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
+ //# 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"}