@empiricalrun/test-gen 0.69.7 → 0.70.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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.70.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2daef2d: feat: upgrade-pkgs tool schema and PR details updated
8
+
9
+ ### Patch Changes
10
+
11
+ - 1348b1a: chore: refactor slack client into internal vs external usage
12
+ - 4274dc5: fix: json report output should exist for video upload
13
+ - 39b105b: fix: auto-merge checks in upgrade package tool
14
+
15
+ ## 0.69.8
16
+
17
+ ### Patch Changes
18
+
19
+ - a2ce1ec: fix: glass pane removal across browser context
20
+ - ff7d9e6: fix: temp file computation for recorder
21
+ - 5e9aeb7: fix: environments list command in test-gen cli
22
+ - @empiricalrun/llm@0.19.3
23
+
3
24
  ## 0.69.7
4
25
 
5
26
  ### Patch Changes
@@ -5,8 +5,4 @@ export declare function runChatAgentForCLI({ useDiskForChatState, selectedModel,
5
5
  useDiskForChatState: boolean;
6
6
  initialPromptContent: string | undefined;
7
7
  }): Promise<void>;
8
- export declare function runChatAgentForDashboard({ chatSessionId, selectedModel, }: {
9
- selectedModel: SupportedChatModels;
10
- chatSessionId: number;
11
- }): Promise<void>;
12
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAyCpC,wBAAsB,yBAAyB,IAAI,OAAO,CACxD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CACvB,CAwBA;AAED,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,iBAkIA;AAuBD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBA6DA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAoCpC,wBAAsB,yBAAyB,IAAI,OAAO,CACxD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CACvB,CAwBA;AAED,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,iBAkIA"}
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fetchEnvironmentVariables = fetchEnvironmentVariables;
4
4
  exports.runChatAgentForCLI = runChatAgentForCLI;
5
- exports.runChatAgentForDashboard = runChatAgentForDashboard;
6
5
  const llm_1 = require("@empiricalrun/llm");
7
6
  const chat_1 = require("@empiricalrun/llm/chat");
8
7
  const picocolors_1 = require("picocolors");
@@ -159,73 +158,3 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
159
158
  const usageSummary = chatModel.getUsageSummary();
160
159
  console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + usageSummary)}`);
161
160
  }
162
- async function getChatSessionFromDashboard(chatSessionId) {
163
- const response = await fetch(`${DASHBOARD_DOMAIN}/api/chat-sessions/${chatSessionId}`, {
164
- headers: {
165
- "Content-Type": "application/json",
166
- Authorization: `Bearer ${process.env.EMPIRICALRUN_API_KEY}`,
167
- },
168
- });
169
- if (!response.ok) {
170
- throw new Error(`Failed to get chat session: ${response.statusText}`);
171
- }
172
- const data = await response.json();
173
- return data.data.chat_session;
174
- }
175
- async function runChatAgentForDashboard({ chatSessionId, selectedModel, }) {
176
- const chatSession = await getChatSessionFromDashboard(chatSessionId);
177
- let chatState = chatSession.chat_state;
178
- // If not already canonical, migrate to canonical format
179
- if (!chatState.version || chatState.version !== state_1.LATEST_CHAT_STATE_VERSION) {
180
- chatState = (0, state_1.migrateChatState)(chatState);
181
- }
182
- const branchName = chatSession.branch_name;
183
- const trace = llm_1.langfuseInstance?.trace({
184
- id: chatSession.langfuse_trace_id,
185
- name: "chat_agent",
186
- input: chatState,
187
- tags: [selectedModel, "chat_agent"],
188
- metadata: {
189
- chatSessionId,
190
- },
191
- });
192
- if (!process.env.EMPIRICALRUN_API_KEY) {
193
- throw new Error("EMPIRICALRUN_API_KEY is not set");
194
- }
195
- const toolCallService = new tool_call_service_1.ToolCallService({
196
- chatSessionId,
197
- selectedModel,
198
- branchName,
199
- repoPath: process.cwd(),
200
- apiKey: process.env.EMPIRICALRUN_API_KEY,
201
- trace,
202
- featureFlags: [],
203
- });
204
- await (0, git_1.checkoutBranch)(branchName, process.cwd());
205
- let chatModel = (0, chat_1.createChatModel)(chatState.messages, selectedModel);
206
- let reporterFunc = async (chatState, latest) => {
207
- const response = await fetch(`${DASHBOARD_DOMAIN}/api/chat-sessions/${chatSessionId}`, {
208
- method: "PATCH",
209
- body: JSON.stringify({
210
- chat_state: chatState,
211
- last_assistant_message: latest?.textMessage,
212
- }),
213
- headers: {
214
- "Content-Type": "application/json",
215
- Authorization: `Bearer ${process.env.EMPIRICALRUN_API_KEY}`,
216
- },
217
- });
218
- const data = await response.json();
219
- console.log(`Patch request sent for chat session: ${JSON.stringify(data)}`);
220
- };
221
- const fileInfo = await (0, file_tree_1.getFileInfoFromFS)(process.cwd());
222
- await (0, agent_loop_1.chatAgentLoop)({
223
- chatModel,
224
- selectedModel,
225
- reporter: reporterFunc,
226
- trace,
227
- toolCallService,
228
- fileInfo,
229
- isToolExecutionRemote: false,
230
- });
231
- }
@@ -1 +1 @@
1
- {"version":3,"file":"for-recorder.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/for-recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIvC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,qBAAa,iCAAiC;IAO1C,OAAO,CAAC,eAAe;IANzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,iBAAiB,CAAqB;gBAGpC,eAAe,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;YAKtD,QAAQ;IAWhB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;CAyCxC"}
1
+ {"version":3,"file":"for-recorder.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/for-recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIvC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,qBAAa,iCAAiC;IAO1C,OAAO,CAAC,eAAe;IANzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,iBAAiB,CAAqB;gBAGpC,eAAe,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;YAKtD,QAAQ;IAWhB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;CA0CxC"}
@@ -47,27 +47,8 @@ class PlaywrightPauseCodegenForRecorder {
47
47
  }
48
48
  async startPlaywrightCodegen(page) {
49
49
  // TODO: Merge this with the other page.pause oriented codegen
50
- const pausePromise = page.pause();
51
- await (0, utils_1.sleep)(5_000);
52
- const evaluatePromise = page.evaluate(() => {
53
- // @ts-ignore
54
- console.log(window["__pw_recorderSetMode"]("recording"));
55
- // Remove playwright's glass pane
56
- /**
57
- * x-pw-glass
58
- * # inside shadow DOM (closed)
59
- * |----> x-pw-overlay (controls)
60
- * |----> x-pw-highlight (red highlight)
61
- * |----> x-pw-tooltip
62
- */
63
- const glassPane = document.querySelector("x-pw-glass");
64
- if (glassPane) {
65
- const styles = glassPane.getAttribute("style") || "";
66
- glassPane.setAttribute("style", styles + "; display: none;");
67
- }
68
- });
69
- await Promise.all([pausePromise, evaluatePromise]);
70
- await page.addInitScript(() => {
50
+ const browserContext = page.context();
51
+ await browserContext.addInitScript(() => {
71
52
  let displayNoneSet = false;
72
53
  function setDisplayNone() {
73
54
  const glassPane = document.querySelector("x-pw-glass");
@@ -85,6 +66,26 @@ class PlaywrightPauseCodegenForRecorder {
85
66
  setDisplayNone();
86
67
  }, 100);
87
68
  });
69
+ const pausePromise = page.pause();
70
+ await (0, utils_1.sleep)(5_000);
71
+ const startRecordingWithoutGlassPane = page.evaluate(() => {
72
+ // @ts-ignore
73
+ window["__pw_recorderSetMode"]("recording");
74
+ // Remove playwright's glass pane
75
+ /**
76
+ * x-pw-glass
77
+ * # inside shadow DOM (closed)
78
+ * |----> x-pw-overlay (controls)
79
+ * |----> x-pw-highlight (red highlight)
80
+ * |----> x-pw-tooltip
81
+ */
82
+ const glassPane = document.querySelector("x-pw-glass");
83
+ if (glassPane) {
84
+ const styles = glassPane.getAttribute("style") || "";
85
+ glassPane.setAttribute("style", styles + "; display: none;");
86
+ }
87
+ });
88
+ await Promise.all([pausePromise, startRecordingWithoutGlassPane]);
88
89
  }
89
90
  }
90
91
  exports.PlaywrightPauseCodegenForRecorder = PlaywrightPauseCodegenForRecorder;
@@ -1,7 +1,9 @@
1
1
  declare class APIClient {
2
2
  private appUrl;
3
3
  constructor();
4
- request(endpoint: string, options?: RequestInit): Promise<Response>;
4
+ request<T>(endpoint: string, options?: RequestInit): Promise<Response & {
5
+ json(): Promise<T>;
6
+ }>;
5
7
  private makeRequest;
6
8
  ensureAuthenticated(): Promise<void>;
7
9
  private refreshToken;
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/auth/api-client.ts"],"names":[],"mappings":"AAQA,cAAM,SAAS;IACb,OAAO,CAAC,MAAM,CAAS;;IAMjB,OAAO,CACX,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,QAAQ,CAAC;YA2BN,WAAW;IAuBnB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;YAiB5B,YAAY;CA4C3B;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/auth/api-client.ts"],"names":[],"mappings":"AAQA,cAAM,SAAS;IACb,OAAO,CAAC,MAAM,CAAS;;IAMjB,OAAO,CAAC,CAAC,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,QAAQ,GAAG;QAAE,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,CAAC;YA2B/B,WAAW;IAuBnB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;YAiB5B,YAAY;CA4C3B;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function listEnvironments(): Promise<void>;
2
+ //# sourceMappingURL=environments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environments.d.ts","sourceRoot":"","sources":["../../src/bin/environments.ts"],"names":[],"mappings":"AAQA,wBAAsB,gBAAgB,kBAiErC"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listEnvironments = listEnvironments;
4
+ const api_client_1 = require("../auth/api-client");
5
+ const validation_1 = require("../recorder/validation");
6
+ async function listEnvironments() {
7
+ try {
8
+ let repoName = await (0, validation_1.validatePackageJson)(process.cwd());
9
+ const response = await api_client_1.apiClient.request(`/api/environments/list?project_repo_name=${encodeURIComponent(repoName)}`);
10
+ if (!response.ok) {
11
+ console.error(`❌ Failed to fetch environments for repo ${repoName}:`, response.statusText);
12
+ process.exit(1);
13
+ }
14
+ const result = await response.json();
15
+ if (result.data?.environments && result.data.environments.length > 0) {
16
+ result.data.environments.forEach((env, index) => {
17
+ if (index > 0) {
18
+ console.log(""); // Add spacing between environments
19
+ }
20
+ console.log(`Environment: ${env.name}`);
21
+ if (env.latest_build) {
22
+ const commit = env.latest_build.commit
23
+ ? env.latest_build.commit.substring(0, 7)
24
+ : "N/A";
25
+ const branch = env.latest_build.branch || "N/A";
26
+ // Calculate relative time
27
+ const createdAt = new Date(env.latest_build.created_at);
28
+ const now = new Date();
29
+ const diffMs = now.getTime() - createdAt.getTime();
30
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
31
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
32
+ const diffMinutes = Math.floor(diffMs / (1000 * 60));
33
+ let timeAgo;
34
+ if (diffDays > 0) {
35
+ timeAgo = `${diffDays} day${diffDays > 1 ? "s" : ""} ago`;
36
+ }
37
+ else if (diffHours > 0) {
38
+ timeAgo = `${diffHours} hour${diffHours > 1 ? "s" : ""} ago`;
39
+ }
40
+ else if (diffMinutes > 0) {
41
+ timeAgo = `${diffMinutes} minute${diffMinutes > 1 ? "s" : ""} ago`;
42
+ }
43
+ else {
44
+ timeAgo = "Just now";
45
+ }
46
+ console.log(` ┌─ Latest build`);
47
+ console.log(` │ Commit: ${commit}`);
48
+ console.log(` │ Branch: ${branch}`);
49
+ console.log(` │ Created: ${timeAgo}`);
50
+ console.log(` │ URL: ${env.latest_build.build_url}`);
51
+ console.log(` └─`);
52
+ }
53
+ else {
54
+ console.log(` └─ No builds available`);
55
+ }
56
+ });
57
+ }
58
+ else {
59
+ console.log(`No environments found for repository: ${repoName}`);
60
+ }
61
+ }
62
+ catch (error) {
63
+ console.error("❌ Error fetching environments:", error.message);
64
+ process.exit(1);
65
+ }
66
+ }
package/dist/bin/index.js CHANGED
@@ -20,10 +20,10 @@ const run_3 = require("../agent/planner/run");
20
20
  const auth_1 = require("../auth");
21
21
  const api_client_1 = require("../auth/api-client");
22
22
  const recorder_1 = require("../recorder");
23
- const validation_1 = require("../recorder/validation");
24
23
  const reporter_1 = require("../reporter");
25
24
  const session_1 = require("../session");
26
25
  const test_build_1 = require("../test-build");
26
+ const environments_1 = require("./environments");
27
27
  const logger_1 = require("./logger");
28
28
  const setup_1 = require("./setup");
29
29
  const utils_2 = require("./utils");
@@ -31,20 +31,12 @@ const scenarios_1 = require("./utils/scenarios");
31
31
  dotenv_1.default.config({
32
32
  path: [".env.local", ".env"],
33
33
  });
34
- async function runChatAgent({ modelInput, chatSessionId, useDiskForChatState, initialPromptPath, }) {
34
+ async function runChatAgent({ modelInput, useDiskForChatState, initialPromptPath, }) {
35
35
  if (modelInput && !utils_2.ARGS_TO_MODEL_MAP[modelInput]) {
36
36
  throw new Error(`Invalid chat model: ${modelInput}`);
37
37
  }
38
38
  const defaultModel = "claude-3-7-sonnet-20250219";
39
39
  const specifiedModel = modelInput && utils_2.ARGS_TO_MODEL_MAP[modelInput];
40
- if (chatSessionId) {
41
- // If --chat-session-id is provided, we run the chat agent for the dashboard
42
- // and not CLI (where user can input their own prompt)
43
- return await (0, chat_1.runChatAgentForDashboard)({
44
- chatSessionId: Number(chatSessionId),
45
- selectedModel: specifiedModel || defaultModel,
46
- });
47
- }
48
40
  let initialPromptContent = undefined;
49
41
  if (initialPromptPath) {
50
42
  try {
@@ -290,40 +282,8 @@ async function main() {
290
282
  program
291
283
  .command("environments")
292
284
  .description("List environments and their latest builds")
293
- .option("--repo-name <name>", "Repository name to fetch environments for")
294
- .action(async (opts) => {
295
- try {
296
- let repoName;
297
- if (opts.repoName) {
298
- repoName = opts.repoName;
299
- }
300
- else {
301
- // Auto-detect repo name from current directory
302
- repoName = await (0, validation_1.validatePackageJson)(process.cwd());
303
- }
304
- const response = await api_client_1.apiClient.request(`/api/environments/list?project_repo_name=${encodeURIComponent(repoName)}`);
305
- if (!response.ok) {
306
- console.log(await response.text());
307
- console.error(`❌ Failed to fetch environments for repo ${repoName}:`, response.statusText);
308
- process.exit(1);
309
- }
310
- const result = await response.json();
311
- if (result.data.environments && result.data.environments.length > 0) {
312
- result.data.environments.forEach((env) => {
313
- console.log(` Environment: ${env.name}`);
314
- if (env.latest_build) {
315
- console.log(` Latest build: ${env.latest_build.url}`);
316
- }
317
- });
318
- }
319
- else {
320
- console.log(`No environments found for repository: ${repoName}`);
321
- }
322
- }
323
- catch (error) {
324
- console.error("❌ Error fetching environments:", error.message);
325
- process.exit(1);
326
- }
285
+ .action(async () => {
286
+ await (0, environments_1.listEnvironments)();
327
287
  process.exit(0);
328
288
  });
329
289
  program
@@ -350,10 +310,8 @@ async function main() {
350
310
  .option("--chat-model <model>", "LLM to use (claude-3-7, claude-4 or gemini-2.5)")
351
311
  .option("--use-disk-for-chat-state", "Save and load chat state from disk")
352
312
  .option("--initial-prompt <path>", "Path to an initial prompt file (e.g. prompt.md)")
353
- .option("--chat-session-id <chat-session-id>", "Identifier for chat session (fetched from dash.empirical.run)")
354
313
  .action(async (options) => {
355
314
  await runChatAgent({
356
- chatSessionId: options.chatSessionId,
357
315
  modelInput: options.chatModel,
358
316
  useDiskForChatState: options.useDiskForChatState,
359
317
  initialPromptPath: options.initialPrompt,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recorder/index.ts"],"names":[],"mappings":"AA4BA,wBAAsB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,iBAgI3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recorder/index.ts"],"names":[],"mappings":"AAgCA,wBAAsB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,iBA+I3D"}
@@ -29,7 +29,7 @@ async function runRecorder({ name }) {
29
29
  const repoDir = process.cwd();
30
30
  let repoName = "";
31
31
  let fileServer = null;
32
- let tempTestFilePath = "tests/temp-test.spec.ts";
32
+ let tempTestFilePath = "tests/temp-file.spec.ts";
33
33
  process.on("SIGINT", async () => {
34
34
  try {
35
35
  await (0, temp_files_1.deleteTempTestFile)(repoDir, tempTestFilePath);
@@ -86,7 +86,15 @@ async function runRecorder({ name }) {
86
86
  selectedProjects = [selectedProject];
87
87
  selectedProjectObj = projects.find((p) => p.name === selectedProject);
88
88
  if (selectedProjectObj) {
89
- tempTestFilePath = (0, temp_files_1.getTempTestFileLocation)(selectedProjectObj);
89
+ const projectSpecificFile = (0, temp_files_1.getTempTestFileLocation)(selectedProjectObj, repoDir);
90
+ logger_1.logger.debug(`Project specific temp test file path: ${projectSpecificFile}`);
91
+ if (!projectSpecificFile) {
92
+ logger_1.logger.error(`No suitable test directory found for project ${selectedProject}.`);
93
+ process.exit(1);
94
+ }
95
+ else {
96
+ tempTestFilePath = projectSpecificFile;
97
+ }
90
98
  }
91
99
  }
92
100
  // Create temp test file in the determined location
@@ -106,6 +114,7 @@ async function runRecorder({ name }) {
106
114
  IPC_FILE_SERVICE_PORT: availablePort.toString(),
107
115
  },
108
116
  });
117
+ await (0, upload_1.waitForSummaryJson)(repoDir);
109
118
  const videoPaths = (0, upload_1.extractVideoAttachments)(repoDir);
110
119
  let attachments = [];
111
120
  if (videoPaths.length === 0) {
@@ -6,7 +6,8 @@ export type PlaywrightProject = {
6
6
  testDir: string | undefined;
7
7
  teardown: string | undefined;
8
8
  };
9
- export declare function getTempTestFileLocation(selectedProject: PlaywrightProject): string;
9
+ export declare function findSubDirs(dir: string, subDirs?: string[], rootDir?: string): void;
10
+ export declare function getTempTestFileLocation(selectedProject: PlaywrightProject, repoDir: string): string | undefined;
10
11
  export declare function createTempTestFile(port: number, repoDir: string, tempFileRelativePath: string): Promise<void>;
11
12
  export declare function deleteTempTestFile(repoDir: string, tempFileRelativePath: string): Promise<void>;
12
13
  //# sourceMappingURL=temp-files.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"temp-files.d.ts","sourceRoot":"","sources":["../../src/recorder/temp-files.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,CAAC;AAEF,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,iBAAiB,GACjC,MAAM,CA8BR;AAqBD,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,oBAAoB,EAAE,MAAM,iBAkB7B;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,oBAAoB,EAAE,MAAM,iBAS7B"}
1
+ {"version":3,"file":"temp-files.d.ts","sourceRoot":"","sources":["../../src/recorder/temp-files.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,CAAC;AAEF,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,EAAO,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAcN;AAED,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,iBAAiB,EAClC,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,SAAS,CAsDpB;AAqBD,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,oBAAoB,EAAE,MAAM,iBAkB7B;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,oBAAoB,EAAE,MAAM,iBAS7B"}
@@ -3,40 +3,70 @@ 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.findSubDirs = findSubDirs;
6
7
  exports.getTempTestFileLocation = getTempTestFileLocation;
7
8
  exports.createTempTestFile = createTempTestFile;
8
9
  exports.deleteTempTestFile = deleteTempTestFile;
9
10
  const fs_1 = __importDefault(require("fs"));
11
+ const minimatch_1 = require("minimatch");
10
12
  const path_1 = __importDefault(require("path"));
11
13
  const logger_1 = require("../logger");
12
- const glob_1 = require("./glob");
13
- function getTempTestFileLocation(selectedProject) {
14
- const baseTestDir = selectedProject.testDir || "tests";
15
- const normalized = path_1.default.normalize(baseTestDir).endsWith("/")
16
- ? path_1.default.normalize(baseTestDir).slice(0, -1)
17
- : path_1.default.normalize(baseTestDir);
18
- const testDirPattern = `${normalized}/**`;
19
- const testMatchPattern = selectedProject.testMatch
20
- ? Array.isArray(selectedProject.testMatch)
21
- ? selectedProject.testMatch
22
- : [selectedProject.testMatch]
23
- : ["**/*.spec.ts"];
24
- const testIgnorePattern = selectedProject.testIgnore
25
- ? Array.isArray(selectedProject.testIgnore)
26
- ? selectedProject.testIgnore
27
- : [selectedProject.testIgnore]
28
- : [];
29
- const patterns = [
30
- "**/temp-test.spec.ts",
31
- testDirPattern,
32
- ...testMatchPattern,
33
- ...testIgnorePattern.map((p) => `!${p}`),
34
- ];
35
- const common = (0, glob_1.generateMatchingString)(patterns);
36
- if (!common) {
37
- throw new Error("Could not generate a valid test file location. Please check your testMatch and testIgnore patterns.");
14
+ function findSubDirs(dir, subDirs = [], rootDir) {
15
+ const root = rootDir || dir;
16
+ const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
17
+ for (const entry of entries) {
18
+ if (entry.isDirectory()) {
19
+ const fullPath = path_1.default.join(dir, entry.name);
20
+ // Calculate relative path from root directory
21
+ const relativePath = path_1.default.relative(root, fullPath);
22
+ subDirs.push(relativePath);
23
+ // Recursively search subdirectories
24
+ findSubDirs(fullPath, subDirs, root);
25
+ }
26
+ }
27
+ }
28
+ function getTempTestFileLocation(selectedProject, repoDir) {
29
+ const testsDir = path_1.default.join(repoDir, "tests");
30
+ if (!fs_1.default.existsSync(testsDir)) {
31
+ throw new Error(`Tests directory not found: ${testsDir}`);
32
+ }
33
+ const subDirs = [""];
34
+ findSubDirs(testsDir, subDirs);
35
+ const candidatesForTemp = subDirs.map((subDir) => {
36
+ return path_1.default.join("tests", subDir, "temp-file.spec.ts");
37
+ });
38
+ const { testDir, testMatch, testIgnore } = selectedProject;
39
+ for (const candidate of candidatesForTemp) {
40
+ const fullPath = path_1.default.join(repoDir, candidate);
41
+ if (testDir) {
42
+ const fullTestDir = path_1.default.join(repoDir, testDir);
43
+ if (!fullPath.startsWith(fullTestDir)) {
44
+ continue;
45
+ }
46
+ }
47
+ if (testMatch) {
48
+ const testMatchArray = Array.isArray(testMatch) ? testMatch : [testMatch];
49
+ const withStarStar = testMatchArray.map((pattern) =>
50
+ // Playwright adds `**/` prefix to all string patterns
51
+ // https://github.com/microsoft/playwright/blob/3fb78b65847bd4ee373792f0778d51824a4a150b/packages/playwright/src/util.ts#L109
52
+ pattern.startsWith("**/") ? pattern : `**/${pattern}`);
53
+ const matches = withStarStar.some((pattern) => (0, minimatch_1.minimatch)(fullPath, pattern, { dot: true, nocase: true }));
54
+ if (!matches) {
55
+ continue;
56
+ }
57
+ }
58
+ if (testIgnore) {
59
+ const testIgnoreArray = Array.isArray(testIgnore)
60
+ ? testIgnore
61
+ : [testIgnore];
62
+ const withStarStar = testIgnoreArray.map((pattern) => pattern.startsWith("**/") ? pattern : `**/${pattern}`);
63
+ const matches = withStarStar.some((pattern) => (0, minimatch_1.minimatch)(fullPath, pattern, { dot: true, nocase: true }));
64
+ if (matches) {
65
+ continue;
66
+ }
67
+ }
68
+ return candidate;
38
69
  }
39
- return common;
40
70
  }
41
71
  function getFixturesImportPath(tempFileRelativePath) {
42
72
  const tempFileStartsWithSlash = tempFileRelativePath.startsWith("/")
@@ -1,3 +1,4 @@
1
+ export declare function waitForSummaryJson(repoDir: string): Promise<void>;
1
2
  export declare function extractVideoAttachments(repoDir: string): string[];
2
3
  export declare function uploadVideosWithSpinner(videoPaths: string[], testName: string): Promise<void | string[]>;
3
4
  //# sourceMappingURL=upload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/recorder/upload.ts"],"names":[],"mappings":"AAaA,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAoBjE;AA6FD,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC,CAiB1B"}
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/recorder/upload.ts"],"names":[],"mappings":"AAcA,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,iBAmBvD;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAoBjE;AA6FD,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC,CAiB1B"}
@@ -3,6 +3,7 @@ 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.waitForSummaryJson = waitForSummaryJson;
6
7
  exports.extractVideoAttachments = extractVideoAttachments;
7
8
  exports.uploadVideosWithSpinner = uploadVideosWithSpinner;
8
9
  const fs_1 = __importDefault(require("fs"));
@@ -15,9 +16,31 @@ const BUCKET_DOMAINS = {
15
16
  "empirical-assets-staging": "assets-staging.empirical.run",
16
17
  "empirical-assets-production": "assets.empirical.run",
17
18
  };
19
+ const SUMMARY_JSON = (repoDir) => path_1.default.join(repoDir, "summary.json");
20
+ async function waitForSummaryJson(repoDir) {
21
+ const summaryPath = SUMMARY_JSON(repoDir);
22
+ const maxTimeout = 5_000;
23
+ const timerPromise = new Promise((resolve, reject) => {
24
+ setTimeout(() => {
25
+ reject(new Error("Timeout waiting for summary.json"));
26
+ }, maxTimeout);
27
+ });
28
+ const fsExistsPromise = new Promise((resolve) => {
29
+ const checkFile = () => {
30
+ if (fs_1.default.existsSync(summaryPath)) {
31
+ resolve();
32
+ }
33
+ else {
34
+ setTimeout(checkFile, 100);
35
+ }
36
+ };
37
+ checkFile();
38
+ });
39
+ return Promise.race([timerPromise, fsExistsPromise]);
40
+ }
18
41
  function extractVideoAttachments(repoDir) {
19
42
  try {
20
- const summaryPath = path_1.default.join(repoDir, "summary.json");
43
+ const summaryPath = SUMMARY_JSON(repoDir);
21
44
  if (!fs_1.default.existsSync(summaryPath)) {
22
45
  console.log("summary.json not found");
23
46
  return [];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/upgrade-packages/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AA+BvD,eAAO,MAAM,mBAAmB,EAAE,IA+GjC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/upgrade-packages/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAuBvD,eAAO,MAAM,mBAAmB,EAAE,IAiIjC,CAAC"}
@@ -10,58 +10,65 @@ const zod_1 = require("zod");
10
10
  const git_1 = require("../../utils/git");
11
11
  const utils_1 = require("../utils");
12
12
  const utils_2 = require("./utils");
13
- const pkgs = [
14
- "@empiricalrun/playwright-utils",
15
- "@empiricalrun/eslint-config",
16
- "@empiricalrun/typescript-config",
17
- ];
18
- const packageSpecSchema = zod_1.z.union([
19
- zod_1.z.string(),
20
- zod_1.z.object({
21
- name: zod_1.z.string(),
22
- version: zod_1.z.string().optional(),
23
- }),
24
- ]);
13
+ const packageSpecSchema = zod_1.z.object({
14
+ name: zod_1.z.string(),
15
+ version: zod_1.z.string(),
16
+ });
25
17
  const upgradePackagesSchema = zod_1.z.object({
26
- packages: zod_1.z.array(packageSpecSchema),
18
+ packages: zod_1.z.array(packageSpecSchema).min(1),
27
19
  });
28
20
  exports.upgradePackagesTool = {
29
21
  schema: {
30
22
  name: "upgradePackages",
31
- description: `Automatically upgrades specific packages. You can optionally specify a version for each package. If a version is not provided, the package will be upgraded to the latest version. The tool handles the entire workflow: updating package files, committing changes, creating/updating PRs, and managing merges. For patch updates (e.g., 1.0.1 → 1.0.2), it automatically merges the PR and cleans up. For minor/major updates, it leaves the PR open for review. Returns a success message with the PR URL and merge status.`,
23
+ description: `This tool can be used to upgrade NPM packages in this repository. To run this tool, specify a list of packages with their names and version strings. At least one package must be specified.
24
+ This tool handles the entire workflow: editing the package.json file, running "npm install", generating lockfile, committing the changes and creating a pull request. For patch updates (e.g., 1.0.1 → 1.0.2), the tool will automatically merge the PR for minor updates. For major updates, the user needs to review the PR and merge it manually.`,
32
25
  parameters: upgradePackagesSchema,
33
26
  },
34
27
  needsBrowser: false,
35
28
  execute: async ({ input, repoPath, apiKey, }) => {
36
- const { packages } = input;
37
- let packagesToUpdate = packages.length ? packages : pkgs;
38
- const packageSpecs = packagesToUpdate.map((p) => typeof p === "string" ? { name: p, version: undefined } : p);
29
+ const { packages: packagesToUpdate } = input;
39
30
  try {
40
31
  const repoName = path_1.default.basename(repoPath);
41
32
  const branchName = await (0, git_1.getCurrentBranchName)(repoPath);
42
33
  const packageJsonPath = path_1.default.join(repoPath, "package.json");
43
34
  const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf-8"));
44
- for (let { name: pkgName, version } of packageSpecs) {
35
+ const changes = [];
36
+ for (let { name: pkgName, version } of packagesToUpdate) {
45
37
  const isDevDep = !!packageJson.devDependencies?.[pkgName];
46
38
  try {
47
- await (0, utils_2.upgradeAndStagePackage)({
39
+ const updatedPackage = await (0, utils_2.upgradeAndStagePackage)({
48
40
  repoPath,
49
41
  pkgName,
50
42
  version,
51
43
  isDevDep,
52
44
  });
45
+ changes.push(updatedPackage);
53
46
  }
54
47
  catch (err) {
55
48
  console.error(`Failed to upgrade ${pkgName}:`, err);
49
+ return {
50
+ result: `Failed to upgrade ${pkgName}: ${err.message}`,
51
+ isError: true,
52
+ };
56
53
  }
57
54
  }
55
+ const filesChanged = await (0, git_1.getFilesChanged)(repoPath);
56
+ const hasChanges = filesChanged.length > 0;
57
+ if (!hasChanges) {
58
+ return {
59
+ result: "Success: No package changes detected, nothing to commit. Skipping PR creation.",
60
+ isError: false,
61
+ };
62
+ }
58
63
  await (0, git_1.commitFilesAndPushBranch)({
59
- commitMessage: "[create-pull-request] automated change [skip ci]",
64
+ commitMessage: `[upgrade-packages] upgrade ${changes.length} packages [skip ci]`,
60
65
  branchName,
61
66
  files: ["package.json", "package-lock.json"],
62
67
  repoPath,
63
68
  });
64
69
  await new Promise((resolve) => setTimeout(resolve, 5_000));
70
+ const prBody = `Upgraded the following packages:\n\n${changes.map((c) => `- \`${c.name}\` → ${c.version ? c.version : "latest"}`).join("\n")}`;
71
+ const prTitle = `chore: upgrade ${changes.length} package(s)`;
65
72
  let shouldMerge = false;
66
73
  let prNumber = null;
67
74
  let merged = false;
@@ -71,8 +78,8 @@ exports.upgradePackagesTool = {
71
78
  repo: repoName,
72
79
  apiKey,
73
80
  branchName,
74
- title: "Upgrade packages",
75
- body: "Upgrade packages to specified versions",
81
+ title: prTitle,
82
+ body: prBody,
76
83
  labels: ["automated"],
77
84
  });
78
85
  prNumber = pr.number;
@@ -109,7 +116,7 @@ exports.upgradePackagesTool = {
109
116
  ? "created but merge failed"
110
117
  : "created but not merged (non-patch updates)";
111
118
  return {
112
- result: `Success: PR ${status}. Link: ${prLink}`,
119
+ result: `Success: Successfully upgraded ${changes.length} package(s). PR ${status}. Link: ${prLink}`,
113
120
  isError: false,
114
121
  };
115
122
  }
@@ -1,4 +1,10 @@
1
1
  export declare function getLatestVersion(packageName: string): Promise<any>;
2
+ export declare function parsePackageJsonDiff({ diff, }: {
3
+ diff: string;
4
+ }): Record<string, {
5
+ old?: string;
6
+ new?: string;
7
+ }>;
2
8
  export declare function shouldMergePR({ repoName, prNumber, apiKey, }: {
3
9
  repoName: string;
4
10
  prNumber: number;
@@ -9,5 +15,8 @@ export declare function upgradeAndStagePackage({ repoPath, pkgName, version, isD
9
15
  pkgName: string;
10
16
  version?: string;
11
17
  isDevDep: boolean;
12
- }): Promise<void>;
18
+ }): Promise<{
19
+ name: string;
20
+ version: any;
21
+ }>;
13
22
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/upgrade-packages/utils.ts"],"names":[],"mappings":"AAYA,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,gBAczD;AAmED,wBAAsB,aAAa,CAAC,EAClC,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,oBA6BA;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB,iBAUA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/upgrade-packages/utils.ts"],"names":[],"mappings":"AAoBA,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,gBAkBzD;AA8BD,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,GACL,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAuBjD;AAED,wBAAsB,aAAa,CAAC,EAClC,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,oBA8BA;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB;;;GAcA"}
@@ -1,11 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getLatestVersion = getLatestVersion;
4
+ exports.parsePackageJsonDiff = parsePackageJsonDiff;
4
5
  exports.shouldMergePR = shouldMergePR;
5
6
  exports.upgradeAndStagePackage = upgradeAndStagePackage;
6
7
  const child_process_1 = require("child_process");
7
8
  const utils_1 = require("../utils");
8
9
  function isPatchUpdate(currentVersion, newVersion) {
10
+ if (!currentVersion || !newVersion) {
11
+ // If current is undefined, we might be installing a new package
12
+ // Both must be truthy for this to be a patch version update
13
+ return false;
14
+ }
9
15
  const current = currentVersion.replace(/^[\^~]/, "").split(".");
10
16
  const next = newVersion.replace(/^[\^~]/, "").split(".");
11
17
  return (current[0] === next[0] && current[1] === next[1] && current[2] !== next[2]);
@@ -15,7 +21,10 @@ async function getLatestVersion(packageName) {
15
21
  try {
16
22
  const response = await fetch(url);
17
23
  if (!response.ok) {
18
- throw new Error(`Failed to fetch data for package: ${packageName}`);
24
+ const errorMsg = response.status === 404
25
+ ? `Package '${packageName}' not found on npm registry`
26
+ : `Failed to fetch data for package: ${packageName}`;
27
+ throw new Error(errorMsg);
19
28
  }
20
29
  const packageInfo = await response.json();
21
30
  console.log(`Latest version of ${packageName}: ${packageInfo.version}`);
@@ -26,68 +35,63 @@ async function getLatestVersion(packageName) {
26
35
  throw error;
27
36
  }
28
37
  }
29
- async function getPackageJsonChanges({ repoName, prNumber, apiKey, }) {
38
+ async function getGitDiffForPackageJson({ repoName, prNumber, apiKey, }) {
30
39
  const url = `${utils_1.GITHUB_API_BASE}/${repoName}/pulls/${prNumber}/files`;
31
- try {
32
- const files = await (0, utils_1.callGitHubProxy)({
33
- method: "GET",
34
- url: url,
35
- apiKey,
36
- });
37
- if (!files) {
38
- throw new Error("Failed to fetch PR files via proxy");
39
- }
40
- const packageJsonChanges = files.find((file) => file.filename === "package.json");
41
- if (!packageJsonChanges) {
42
- console.log("No package.json changes found in PR");
43
- return null;
44
- }
45
- const patchLines = packageJsonChanges.patch.split("\n");
46
- const changes = {};
47
- // Process the patch lines to find version changes
48
- for (let i = 0; i < patchLines.length; i++) {
49
- const line = patchLines[i];
50
- // Look for lines that change version numbers
51
- const match = line.match(/^[-+]\s*"([^"]+)":\s*"([^"]+)"/);
52
- if (match) {
53
- const [, pkg, version] = match;
54
- const changeType = line.startsWith("+") ? "new" : "old";
55
- if (!changes[pkg]) {
56
- changes[pkg] = {};
57
- }
58
- changes[pkg][changeType] = version;
40
+ const files = await (0, utils_1.callGitHubProxy)({
41
+ method: "GET",
42
+ url: url,
43
+ apiKey,
44
+ });
45
+ if (!files) {
46
+ throw new Error("Failed to fetch PR files via proxy");
47
+ }
48
+ const packageJsonChanges = files.find((file) => file.filename === "package.json");
49
+ if (!packageJsonChanges) {
50
+ console.log("No package.json changes found in PR");
51
+ return null;
52
+ }
53
+ return packageJsonChanges.patch;
54
+ }
55
+ function parsePackageJsonDiff({ diff, }) {
56
+ const patchLines = diff.split("\n");
57
+ const changes = {};
58
+ for (let i = 0; i < patchLines.length; i++) {
59
+ const line = patchLines[i];
60
+ // Look for lines that change version numbers
61
+ const match = line.match(/^[-+]\s*"([^"]+)":\s*"([^"]+)"/);
62
+ if (match) {
63
+ const [, pkg, version] = match;
64
+ if (!pkg) {
65
+ console.warn("No package name found in line:", line);
66
+ continue;
59
67
  }
60
- }
61
- // Filter out any packages that don't have both old and new versions
62
- const validChanges = {};
63
- for (const [pkg, versions] of Object.entries(changes)) {
64
- if (versions.old && versions.new) {
65
- validChanges[pkg] = {
66
- oldVersion: versions.old,
67
- newVersion: versions.new,
68
- };
68
+ const changeType = line.startsWith("+") ? "new" : "old";
69
+ if (!changes[pkg]) {
70
+ changes[pkg] = {};
69
71
  }
72
+ changes[pkg][changeType] = version;
70
73
  }
71
- return validChanges;
72
- }
73
- catch (error) {
74
- console.error(`Error fetching PR changes: ${error.message}`);
75
- throw error;
76
74
  }
75
+ return changes;
77
76
  }
78
77
  async function shouldMergePR({ repoName, prNumber, apiKey, }) {
79
- const changes = await getPackageJsonChanges({
78
+ const gitDiff = await getGitDiffForPackageJson({
80
79
  repoName,
81
80
  prNumber,
82
81
  apiKey,
83
82
  });
84
- if (!changes) {
83
+ if (!gitDiff) {
84
+ console.log("No package.json changes found, skipping merge");
85
+ return false;
86
+ }
87
+ const changes = parsePackageJsonDiff({ diff: gitDiff });
88
+ if (Object.keys(changes).length === 0) {
85
89
  console.log("No package.json changes found, skipping merge");
86
90
  return false;
87
91
  }
88
92
  let allPatchUpdates = true;
89
93
  for (const [pkg, versions] of Object.entries(changes)) {
90
- const { oldVersion, newVersion } = versions;
94
+ const { old: oldVersion, new: newVersion } = versions;
91
95
  if (!isPatchUpdate(oldVersion, newVersion)) {
92
96
  console.log(`${pkg}: ${oldVersion} -> ${newVersion} is not a patch update`);
93
97
  allPatchUpdates = false;
@@ -103,4 +107,8 @@ async function upgradeAndStagePackage({ repoPath, pkgName, version, isDevDep, })
103
107
  (0, child_process_1.execSync)(`npm i ${pkgName}@${pkgVersion} ${isDevDep ? "--save-dev" : "--save"}`, { cwd: repoPath });
104
108
  console.log(`Updated package: ${pkgName} version to ${pkgVersion} in ${repoPath}`);
105
109
  (0, child_process_1.execSync)("git add package.json package-lock.json", { cwd: repoPath });
110
+ return {
111
+ name: pkgName,
112
+ version: pkgVersion,
113
+ };
106
114
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-gen",
3
- "version": "0.69.7",
3
+ "version": "0.70.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -82,7 +82,7 @@
82
82
  "playwright": "1.53.0",
83
83
  "serve-handler": "^6.1.6",
84
84
  "ts-patch": "^3.3.0",
85
- "@empiricalrun/shared-types": "0.6.1"
85
+ "@empiricalrun/shared-types": "0.6.2"
86
86
  },
87
87
  "scripts": {
88
88
  "dev": "tspc --build --watch",
@@ -1 +1 @@
1
- {"root":["./src/index.ts","./src/logger.ts","./src/actions/assert.ts","./src/actions/click.ts","./src/actions/done.ts","./src/actions/fill.ts","./src/actions/goto.ts","./src/actions/hover.ts","./src/actions/index.ts","./src/actions/next-task.ts","./src/actions/press.ts","./src/actions/skill.ts","./src/actions/text-content.ts","./src/actions/constants/index.ts","./src/actions/utils/index.ts","./src/agent/browsing/index.ts","./src/agent/browsing/run.ts","./src/agent/browsing/utils.ts","./src/agent/chat/agent-loop.ts","./src/agent/chat/exports.ts","./src/agent/chat/index.ts","./src/agent/chat/models.ts","./src/agent/chat/state.ts","./src/agent/chat/types.ts","./src/agent/chat/utils.ts","./src/agent/chat/prompt/index.ts","./src/agent/chat/prompt/pw-utils-docs.ts","./src/agent/chat/prompt/repo.ts","./src/agent/codegen/create-test-block.ts","./src/agent/codegen/fix-ts-errors.ts","./src/agent/codegen/generate-code-apply-changes.ts","./src/agent/codegen/lexical-scoped-vars.ts","./src/agent/codegen/repo-edit.ts","./src/agent/codegen/run.ts","./src/agent/codegen/skills-retriever.ts","./src/agent/codegen/test-update-feedback.ts","./src/agent/codegen/types.ts","./src/agent/codegen/update-flow.ts","./src/agent/codegen/use-skill.ts","./src/agent/codegen/utils.ts","./src/agent/cua/computer.ts","./src/agent/cua/index.ts","./src/agent/cua/model.ts","./src/agent/cua/pw-codegen/element-from-point.ts","./src/agent/cua/pw-codegen/types.ts","./src/agent/cua/pw-codegen/pw-pause/for-recorder.ts","./src/agent/cua/pw-codegen/pw-pause/index.ts","./src/agent/cua/pw-codegen/pw-pause/ipc.ts","./src/agent/cua/pw-codegen/pw-pause/patch.ts","./src/agent/cua/pw-codegen/pw-pause/types.ts","./src/agent/cua/pw-codegen/pw-pause/utils.ts","./src/agent/diagnosis-agent/index.ts","./src/agent/diagnosis-agent/strict-mode-violation.ts","./src/agent/enrich-prompt/index.ts","./src/agent/enrich-prompt/utils.ts","./src/agent/infer-agent/index.ts","./src/agent/master/action-tool-calls.ts","./src/agent/master/element-annotation.ts","./src/agent/master/execute-browser-action.ts","./src/agent/master/execute-skill-action.ts","./src/agent/master/next-action.ts","./src/agent/master/planner.ts","./src/agent/master/run.ts","./src/agent/master/scroller.ts","./src/agent/master/with-hints.ts","./src/agent/master/browser-tests/cua.spec.ts","./src/agent/master/browser-tests/fixtures.ts","./src/agent/master/browser-tests/index.spec.ts","./src/agent/master/browser-tests/skills.spec.ts","./src/agent/master/icon-descriptor/index.ts","./src/agent/master/icon-descriptor/normalize-svg.ts","./src/agent/planner/run-time-planner.ts","./src/agent/planner/run.ts","./src/artifacts/index.ts","./src/artifacts/utils.ts","./src/auth/api-client.ts","./src/auth/cli-auth.ts","./src/auth/index.ts","./src/auth/token-store.ts","./src/bin/index.ts","./src/bin/setup.ts","./src/bin/logger/index.ts","./src/bin/utils/context.ts","./src/bin/utils/index.ts","./src/bin/utils/fs/index.ts","./src/bin/utils/platform/web/index.ts","./src/bin/utils/platform/web/test-files/ts-path-import-validate.ts","./src/bin/utils/scenarios/index.ts","./src/browser-injected-scripts/annotate-elements.spec.ts","./src/constants/index.ts","./src/errors/index.ts","./src/evals/add-scenario-agent.evals.ts","./src/evals/append-create-test-agent.evals.ts","./src/evals/fetch-pom-skills-agent.evals.ts","./src/evals/infer-master-or-code-agent.evals.ts","./src/evals/master-agent.evals.ts","./src/evals/type.ts","./src/evals/update-scenario-agent.evals.ts","./src/file/client.ts","./src/file/server.ts","./src/human-in-the-loop/cli.ts","./src/human-in-the-loop/index.ts","./src/human-in-the-loop/ipc.ts","./src/page/index.ts","./src/prompts/lib/ts-transformer.ts","./src/recorder/env-variables.ts","./src/recorder/glob.ts","./src/recorder/index.ts","./src/recorder/request.ts","./src/recorder/temp-files.ts","./src/recorder/upload.ts","./src/recorder/validation.ts","./src/reporter/index.ts","./src/reporter/lib.ts","./src/session/index.ts","./src/test-build/index.ts","./src/tool-call-service/index.ts","./src/tool-call-service/utils.ts","./src/tools/commit-and-create-pr.ts","./src/tools/diagnosis-fetcher.ts","./src/tools/download-build.ts","./src/tools/list-environments.ts","./src/tools/str_replace_editor.ts","./src/tools/test-gen-browser.ts","./src/tools/test-run.ts","./src/tools/grep/index.ts","./src/tools/grep/ripgrep/index.ts","./src/tools/grep/ripgrep/types.ts","./src/tools/test-run-fetcher/index.ts","./src/tools/test-run-fetcher/types.ts","./src/tools/upgrade-packages/index.ts","./src/tools/upgrade-packages/utils.ts","./src/tools/utils/index.ts","./src/types/handlebars.d.ts","./src/types/index.ts","./src/uploader/index.ts","./src/uploader/utils.ts","./src/utils/checkpoint.ts","./src/utils/env.ts","./src/utils/exec.ts","./src/utils/file-tree.ts","./src/utils/file.ts","./src/utils/git.ts","./src/utils/html.ts","./src/utils/index.ts","./src/utils/json.ts","./src/utils/repo-tree.ts","./src/utils/slug.ts","./src/utils/string.ts","./src/utils/stripAnsi.ts"],"version":"5.8.3"}
1
+ {"root":["./src/index.ts","./src/logger.ts","./src/actions/assert.ts","./src/actions/click.ts","./src/actions/done.ts","./src/actions/fill.ts","./src/actions/goto.ts","./src/actions/hover.ts","./src/actions/index.ts","./src/actions/next-task.ts","./src/actions/press.ts","./src/actions/skill.ts","./src/actions/text-content.ts","./src/actions/constants/index.ts","./src/actions/utils/index.ts","./src/agent/browsing/index.ts","./src/agent/browsing/run.ts","./src/agent/browsing/utils.ts","./src/agent/chat/agent-loop.ts","./src/agent/chat/exports.ts","./src/agent/chat/index.ts","./src/agent/chat/models.ts","./src/agent/chat/state.ts","./src/agent/chat/types.ts","./src/agent/chat/utils.ts","./src/agent/chat/prompt/index.ts","./src/agent/chat/prompt/pw-utils-docs.ts","./src/agent/chat/prompt/repo.ts","./src/agent/codegen/create-test-block.ts","./src/agent/codegen/fix-ts-errors.ts","./src/agent/codegen/generate-code-apply-changes.ts","./src/agent/codegen/lexical-scoped-vars.ts","./src/agent/codegen/repo-edit.ts","./src/agent/codegen/run.ts","./src/agent/codegen/skills-retriever.ts","./src/agent/codegen/test-update-feedback.ts","./src/agent/codegen/types.ts","./src/agent/codegen/update-flow.ts","./src/agent/codegen/use-skill.ts","./src/agent/codegen/utils.ts","./src/agent/cua/computer.ts","./src/agent/cua/index.ts","./src/agent/cua/model.ts","./src/agent/cua/pw-codegen/element-from-point.ts","./src/agent/cua/pw-codegen/types.ts","./src/agent/cua/pw-codegen/pw-pause/for-recorder.ts","./src/agent/cua/pw-codegen/pw-pause/index.ts","./src/agent/cua/pw-codegen/pw-pause/ipc.ts","./src/agent/cua/pw-codegen/pw-pause/patch.ts","./src/agent/cua/pw-codegen/pw-pause/types.ts","./src/agent/cua/pw-codegen/pw-pause/utils.ts","./src/agent/diagnosis-agent/index.ts","./src/agent/diagnosis-agent/strict-mode-violation.ts","./src/agent/enrich-prompt/index.ts","./src/agent/enrich-prompt/utils.ts","./src/agent/infer-agent/index.ts","./src/agent/master/action-tool-calls.ts","./src/agent/master/element-annotation.ts","./src/agent/master/execute-browser-action.ts","./src/agent/master/execute-skill-action.ts","./src/agent/master/next-action.ts","./src/agent/master/planner.ts","./src/agent/master/run.ts","./src/agent/master/scroller.ts","./src/agent/master/with-hints.ts","./src/agent/master/browser-tests/cua.spec.ts","./src/agent/master/browser-tests/fixtures.ts","./src/agent/master/browser-tests/index.spec.ts","./src/agent/master/browser-tests/skills.spec.ts","./src/agent/master/icon-descriptor/index.ts","./src/agent/master/icon-descriptor/normalize-svg.ts","./src/agent/planner/run-time-planner.ts","./src/agent/planner/run.ts","./src/artifacts/index.ts","./src/artifacts/utils.ts","./src/auth/api-client.ts","./src/auth/cli-auth.ts","./src/auth/index.ts","./src/auth/token-store.ts","./src/bin/environments.ts","./src/bin/index.ts","./src/bin/setup.ts","./src/bin/logger/index.ts","./src/bin/utils/context.ts","./src/bin/utils/index.ts","./src/bin/utils/fs/index.ts","./src/bin/utils/platform/web/index.ts","./src/bin/utils/platform/web/test-files/ts-path-import-validate.ts","./src/bin/utils/scenarios/index.ts","./src/browser-injected-scripts/annotate-elements.spec.ts","./src/constants/index.ts","./src/errors/index.ts","./src/evals/add-scenario-agent.evals.ts","./src/evals/append-create-test-agent.evals.ts","./src/evals/fetch-pom-skills-agent.evals.ts","./src/evals/infer-master-or-code-agent.evals.ts","./src/evals/master-agent.evals.ts","./src/evals/type.ts","./src/evals/update-scenario-agent.evals.ts","./src/file/client.ts","./src/file/server.ts","./src/human-in-the-loop/cli.ts","./src/human-in-the-loop/index.ts","./src/human-in-the-loop/ipc.ts","./src/page/index.ts","./src/prompts/lib/ts-transformer.ts","./src/recorder/env-variables.ts","./src/recorder/index.ts","./src/recorder/request.ts","./src/recorder/temp-files.ts","./src/recorder/upload.ts","./src/recorder/validation.ts","./src/reporter/index.ts","./src/reporter/lib.ts","./src/session/index.ts","./src/test-build/index.ts","./src/tool-call-service/index.ts","./src/tool-call-service/utils.ts","./src/tools/commit-and-create-pr.ts","./src/tools/diagnosis-fetcher.ts","./src/tools/download-build.ts","./src/tools/list-environments.ts","./src/tools/str_replace_editor.ts","./src/tools/test-gen-browser.ts","./src/tools/test-run.ts","./src/tools/grep/index.ts","./src/tools/grep/ripgrep/index.ts","./src/tools/grep/ripgrep/types.ts","./src/tools/test-run-fetcher/index.ts","./src/tools/test-run-fetcher/types.ts","./src/tools/upgrade-packages/index.ts","./src/tools/upgrade-packages/utils.ts","./src/tools/utils/index.ts","./src/types/handlebars.d.ts","./src/types/index.ts","./src/uploader/index.ts","./src/uploader/utils.ts","./src/utils/checkpoint.ts","./src/utils/env.ts","./src/utils/exec.ts","./src/utils/file-tree.ts","./src/utils/file.ts","./src/utils/git.ts","./src/utils/html.ts","./src/utils/index.ts","./src/utils/json.ts","./src/utils/repo-tree.ts","./src/utils/slug.ts","./src/utils/string.ts","./src/utils/stripAnsi.ts"],"version":"5.8.3"}
@@ -1,2 +0,0 @@
1
- export declare function generateMatchingString(patterns: string[]): string | null;
2
- //# sourceMappingURL=glob.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../src/recorder/glob.ts"],"names":[],"mappings":"AAGA,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CA8FxE"}
@@ -1,74 +0,0 @@
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.generateMatchingString = generateMatchingString;
7
- const minimatch_1 = require("minimatch");
8
- const path_1 = __importDefault(require("path"));
9
- function generateMatchingString(patterns) {
10
- // Separate positive and negative patterns
11
- const positivePatterns = patterns.filter((p) => !p.startsWith("!"));
12
- const negativePatterns = patterns
13
- .filter((p) => p.startsWith("!"))
14
- .map((p) => p.slice(1));
15
- // Start with the most specific pattern - temp-test.spec.ts
16
- const tempTestPattern = positivePatterns.find((p) => p.includes("temp-test.spec.ts"));
17
- if (!tempTestPattern) {
18
- return null;
19
- }
20
- // Extract the base directory from the testDirPattern
21
- const testDirPattern = positivePatterns.find((p) => p.includes("/**"));
22
- if (!testDirPattern) {
23
- return null;
24
- }
25
- // Get the base directory (everything before /**)
26
- const baseDir = testDirPattern.replace("/**", "");
27
- // Generate a simple path that matches all positive patterns
28
- let candidatePath = path_1.default.join(baseDir, "temp-test.spec.ts");
29
- // Check if it matches all positive patterns
30
- for (const pattern of positivePatterns) {
31
- if (!(0, minimatch_1.minimatch)(candidatePath, pattern)) {
32
- // If it doesn't match, try to adjust the path
33
- // For patterns like **/*.spec.ts, we might need to add subdirectories
34
- if (pattern.includes("**/")) {
35
- // Try adding a subdirectory
36
- candidatePath = path_1.default.join(baseDir, "e2e", "temp-test.spec.ts");
37
- }
38
- }
39
- }
40
- // Check against negative patterns
41
- for (const pattern of negativePatterns) {
42
- if ((0, minimatch_1.minimatch)(candidatePath, pattern)) {
43
- // If it matches a negative pattern, try to find an alternative path
44
- // Add a different subdirectory or modify the path
45
- candidatePath = path_1.default.join(baseDir, "integration", "temp-test.spec.ts");
46
- // Re-check against all patterns
47
- const matchesAllPositive = positivePatterns.every((p) => (0, minimatch_1.minimatch)(candidatePath, p));
48
- const matchesNoNegative = !negativePatterns.some((p) => (0, minimatch_1.minimatch)(candidatePath, p));
49
- if (!matchesAllPositive || !matchesNoNegative) {
50
- // Try another path
51
- candidatePath = path_1.default.join(baseDir, "specs", "temp-test.spec.ts");
52
- }
53
- }
54
- }
55
- // Final validation
56
- const matchesAllPositive = positivePatterns.every((p) => (0, minimatch_1.minimatch)(candidatePath, p));
57
- const matchesNoNegative = !negativePatterns.some((p) => (0, minimatch_1.minimatch)(candidatePath, p));
58
- if (matchesAllPositive && matchesNoNegative) {
59
- return candidatePath;
60
- }
61
- // If we still can't find a valid path, try a more systematic approach
62
- const subdirs = ["", "e2e", "integration", "specs", "playwright", "tests"];
63
- for (const subdir of subdirs) {
64
- const testPath = subdir
65
- ? path_1.default.join(baseDir, subdir, "temp-test.spec.ts")
66
- : path_1.default.join(baseDir, "temp-test.spec.ts");
67
- const matchesAllPositive = positivePatterns.every((p) => (0, minimatch_1.minimatch)(testPath, p));
68
- const matchesNoNegative = !negativePatterns.some((p) => (0, minimatch_1.minimatch)(testPath, p));
69
- if (matchesAllPositive && matchesNoNegative) {
70
- return testPath;
71
- }
72
- }
73
- return null;
74
- }