@empiricalrun/test-gen 0.67.0 → 0.69.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 (53) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/agent/chat/exports.d.ts +8 -8
  3. package/dist/agent/chat/exports.d.ts.map +1 -1
  4. package/dist/agent/chat/exports.js +10 -7
  5. package/dist/agent/chat/index.d.ts.map +1 -1
  6. package/dist/agent/chat/index.js +5 -2
  7. package/dist/agent/chat/state.d.ts +4 -2
  8. package/dist/agent/chat/state.d.ts.map +1 -1
  9. package/dist/agent/chat/state.js +10 -2
  10. package/dist/agent/chat/utils.d.ts +5 -1
  11. package/dist/agent/chat/utils.d.ts.map +1 -1
  12. package/dist/agent/chat/utils.js +22 -0
  13. package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.d.ts.map +1 -1
  14. package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.js +24 -3
  15. package/dist/agent/cua/pw-codegen/pw-pause/patch.d.ts.map +1 -1
  16. package/dist/agent/cua/pw-codegen/pw-pause/patch.js +4 -6
  17. package/dist/auth/api-client.d.ts +1 -2
  18. package/dist/auth/api-client.d.ts.map +1 -1
  19. package/dist/auth/api-client.js +15 -45
  20. package/dist/auth/index.d.ts +1 -1
  21. package/dist/auth/index.d.ts.map +1 -1
  22. package/dist/auth/index.js +1 -4
  23. package/dist/bin/index.js +104 -108
  24. package/dist/bin/setup.d.ts +4 -0
  25. package/dist/bin/setup.d.ts.map +1 -0
  26. package/dist/bin/setup.js +86 -0
  27. package/dist/bin/utils/index.d.ts +2 -7
  28. package/dist/bin/utils/index.d.ts.map +1 -1
  29. package/dist/bin/utils/index.js +1 -9
  30. package/dist/file/server.js +4 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -1
  33. package/dist/logger.d.ts +3 -0
  34. package/dist/logger.d.ts.map +1 -0
  35. package/dist/logger.js +11 -0
  36. package/dist/recorder/env-variables.d.ts +2 -0
  37. package/dist/recorder/env-variables.d.ts.map +1 -0
  38. package/dist/recorder/env-variables.js +29 -0
  39. package/dist/recorder/index.d.ts.map +1 -1
  40. package/dist/recorder/index.js +25 -34
  41. package/dist/recorder/request.d.ts +2 -1
  42. package/dist/recorder/request.d.ts.map +1 -1
  43. package/dist/recorder/request.js +15 -21
  44. package/dist/recorder/temp-files.d.ts.map +1 -1
  45. package/dist/recorder/temp-files.js +5 -4
  46. package/dist/recorder/upload.d.ts +1 -0
  47. package/dist/recorder/upload.d.ts.map +1 -1
  48. package/dist/recorder/upload.js +23 -11
  49. package/dist/recorder/validation.d.ts +1 -1
  50. package/dist/recorder/validation.d.ts.map +1 -1
  51. package/dist/recorder/validation.js +9 -4
  52. package/package.json +6 -4
  53. package/tsconfig.tsbuildinfo +1 -1
package/dist/bin/index.js CHANGED
@@ -17,26 +17,18 @@ const enrich_prompt_1 = require("../agent/enrich-prompt");
17
17
  const infer_agent_1 = require("../agent/infer-agent");
18
18
  const run_3 = require("../agent/planner/run");
19
19
  const auth_1 = require("../auth");
20
+ const api_client_1 = require("../auth/api-client");
20
21
  const recorder_1 = require("../recorder");
21
22
  const reporter_1 = require("../reporter");
22
23
  const session_1 = require("../session");
23
24
  const test_build_1 = require("../test-build");
24
25
  const logger_1 = require("./logger");
26
+ const setup_1 = require("./setup");
25
27
  const utils_2 = require("./utils");
26
28
  const scenarios_1 = require("./utils/scenarios");
27
29
  dotenv_1.default.config({
28
30
  path: [".env.local", ".env"],
29
31
  });
30
- const flushEvents = async () => {
31
- await (0, llm_1.flushAllTraces)();
32
- };
33
- function setupProcessListeners(cleanup) {
34
- const events = ["beforeExit", "exit", "SIGINT", "SIGTERM"];
35
- events.forEach((event) => process.once(event, cleanup));
36
- return () => {
37
- events.forEach((event) => process.removeListener(event, cleanup));
38
- };
39
- }
40
32
  async function runChatAgent({ modelInput, chatSessionId, useDiskForChatState, initialPromptPath, }) {
41
33
  if (modelInput && !utils_2.ARGS_TO_MODEL_MAP[modelInput]) {
42
34
  throw new Error(`Invalid chat model: ${modelInput}`);
@@ -201,13 +193,11 @@ async function runAgentsWorkflow(testGenConfig, testGenToken) {
201
193
  return agent;
202
194
  }
203
195
  async function main() {
204
- const removeListeners = setupProcessListeners(flushEvents);
205
196
  await (0, utils_2.printBanner)();
206
197
  const program = new commander_1.Command();
207
- // Add authentication commands
208
198
  program
209
199
  .command("login")
210
- .description("Authenticate with your Empirical Run account")
200
+ .description("Authenticate with your Empirical account")
211
201
  .action(async () => {
212
202
  console.log("🔐 Starting authentication...\n");
213
203
  try {
@@ -232,7 +222,7 @@ async function main() {
232
222
  });
233
223
  program
234
224
  .command("logout")
235
- .description("Sign out of your Empirical Run account")
225
+ .description("Sign out of your Empirical account")
236
226
  .action(async () => {
237
227
  try {
238
228
  await (0, auth_1.logout)();
@@ -265,118 +255,124 @@ async function main() {
265
255
  console.error("❌ Error checking auth status:", error.message);
266
256
  process.exit(1);
267
257
  }
268
- removeListeners();
269
- await (0, llm_1.flushAllTraces)();
270
- await (0, logger_1.waitForLogsToFlush)();
271
258
  process.exit(0);
272
259
  });
273
260
  program
274
- .command("auth-test")
275
- .description("Test authentication by making an API call")
261
+ .command("record")
262
+ .description("Record a new test case")
263
+ .option("--name <string>", "Name of the test case")
264
+ .action(async (opts) => {
265
+ const options = await (0, utils_2.validateAndCompleteCliOptions)(opts, ["name"]);
266
+ await (0, recorder_1.runRecorder)({ name: options.name });
267
+ process.exit(0);
268
+ });
269
+ program
270
+ .command("repos")
271
+ .description("List your projects and repositories")
276
272
  .action(async () => {
277
273
  try {
278
- console.log("🔍 Testing authentication with API call...\n");
279
- const data = await (0, auth_1.authenticatedFetch)("/requests");
280
- console.log(" API call successful!");
281
- console.log("📄 Response:", JSON.stringify(data, null, 2));
274
+ const response = await api_client_1.apiClient.request("/api/projects");
275
+ if (!response.ok) {
276
+ console.error(" Failed to fetch projects:", response.statusText);
277
+ process.exit(1);
278
+ }
279
+ const result = await response.json();
280
+ result.data.projects.forEach((project) => {
281
+ console.log(` ${project.repo_name}`);
282
+ });
282
283
  }
283
284
  catch (error) {
284
- console.error("❌ API call failed:", error.message);
285
- if (error.message.includes("Authentication required")) {
286
- console.log('\n💡 Tip: Run "npx @empiricalrun/test-gen login" to authenticate');
287
- }
285
+ console.error("❌ Error fetching projects:", error.message);
288
286
  process.exit(1);
289
287
  }
290
- removeListeners();
291
- await (0, llm_1.flushAllTraces)();
292
- await (0, logger_1.waitForLogsToFlush)();
293
288
  process.exit(0);
294
289
  });
295
290
  program
296
- .option("--token <token>", "Test generation token")
297
- .option("--prompt <prompt>", "Prompt for the chat agent")
298
- .option("--name <test-name>", "Name of the test case")
299
- .option("--file <test-file>", "File path of the test case (inside tests dir)")
300
- .option("--suites <suites>", "Comma separated list of describe blocks")
301
- .option("--use-chat", "Use chat agent (and not the workflow)")
302
- .option("--use-recorder", "Run the recorder flow to create a request")
303
- .option("--chat-session-id <chat-session-id>", "Identifier for chat session (fetched from dash.empirical.run)")
291
+ .command("chat-agent")
292
+ .description("Run the chat agent")
293
+ .option("--chat-model <model>", "LLM to use (claude-3-7, claude-4 or gemini-2.5)")
304
294
  .option("--use-disk-for-chat-state", "Save and load chat state from disk")
305
- .option("--chat-model <model>", "Chat model to use (claude-3-7-sonnet-20250219 or claude-3-5-sonnet-20241022 or gemini-2.5-pro-preview-06-05)")
306
295
  .option("--initial-prompt <path>", "Path to an initial prompt file (e.g. prompt.md)")
307
- .option("--with-retry", "Use the retry strategy")
308
- .parse(process.argv);
309
- // Check if a command was executed (auth commands exit early)
310
- const options = program.opts();
311
- // Only proceed with main workflow if no auth command was executed
312
- const completedOptions = await (0, utils_2.validateAndCompleteCliOptions)(options);
313
- const testGenConfig = completedOptions.token
314
- ? (0, scenarios_1.loadTestConfigs)(completedOptions.token)
315
- : (0, scenarios_1.buildTestConfigFromOptions)(completedOptions);
316
- const testGenToken = completedOptions.token
317
- ? completedOptions.token
318
- : (0, scenarios_1.buildTokenFromOptions)(completedOptions);
319
- (0, reporter_1.setReporterConfig)({
320
- projectRepoName: testGenConfig.options?.metadata.projectRepoName,
321
- testSessionId: testGenConfig.options?.metadata.testSessionId,
322
- generationId: testGenConfig.options?.metadata.generationId,
296
+ .option("--chat-session-id <chat-session-id>", "Identifier for chat session (fetched from dash.empirical.run)")
297
+ .action(async (options) => {
298
+ await runChatAgent({
299
+ chatSessionId: options.chatSessionId,
300
+ modelInput: options.chatModel,
301
+ useDiskForChatState: options.useDiskForChatState,
302
+ initialPromptPath: options.initialPrompt,
303
+ });
304
+ process.exit(0);
323
305
  });
324
- (0, session_1.setSessionDetails)({
325
- testCaseId: testGenConfig.testCase.id,
326
- sessionId: testGenConfig.options?.metadata.testSessionId,
327
- generationId: testGenConfig.options?.metadata.generationId,
328
- projectRepoName: testGenConfig.options?.metadata.projectRepoName,
306
+ program
307
+ .command("setup")
308
+ .description("Clone a repository from your projects")
309
+ .option("--repo-name <string>", "Name of the repository to clone")
310
+ .action(async (opts) => {
311
+ const options = await (0, utils_2.validateAndCompleteCliOptions)(opts, ["repoName"]);
312
+ await (0, setup_1.runSetup)({ repoName: options.repoName });
313
+ process.exit(0);
329
314
  });
330
- if (testGenConfig.build?.url) {
331
- // Download the build if repo has a download script
332
- await (0, test_build_1.downloadBuild)({
333
- buildUrl: testGenConfig.build.url,
334
- repoPath: process.cwd(),
335
- apiKey: process.env.EMPIRICALRUN_API_KEY,
336
- });
337
- }
338
- if (completedOptions.useRecorder) {
339
- await (0, recorder_1.runRecorder)({ name: completedOptions.name });
340
- return;
341
- }
342
- if (completedOptions.useChat) {
343
- await runChatAgent({
344
- chatSessionId: completedOptions.chatSessionId,
345
- modelInput: completedOptions.chatModel,
346
- useDiskForChatState: completedOptions.useDiskForChatState,
347
- initialPromptPath: completedOptions.initialPrompt,
315
+ program
316
+ .command("legacy")
317
+ .description("Run the legacy workflows")
318
+ .option("-t, --token <token>", "Test generation token")
319
+ .action(async (opts) => {
320
+ const options = await (0, utils_2.validateAndCompleteCliOptions)(opts);
321
+ const testGenConfig = options.token
322
+ ? (0, scenarios_1.loadTestConfigs)(options.token)
323
+ : (0, scenarios_1.buildTestConfigFromOptions)(options);
324
+ const testGenToken = options.token
325
+ ? options.token
326
+ : (0, scenarios_1.buildTokenFromOptions)(options);
327
+ (0, reporter_1.setReporterConfig)({
328
+ projectRepoName: testGenConfig.options?.metadata.projectRepoName,
329
+ testSessionId: testGenConfig.options?.metadata.testSessionId,
330
+ generationId: testGenConfig.options?.metadata.generationId,
348
331
  });
349
- return;
350
- }
351
- let agentUsed;
352
- let testGenFailed = false;
353
- try {
354
- agentUsed = await runAgentsWorkflow(testGenConfig, testGenToken);
355
- }
356
- catch (e) {
357
- testGenFailed = true;
358
- new logger_1.CustomLogger().error(`Failed to generate test for the scenario. ${process.env.LOG_URL ? `[view log](${process.env.LOG_URL})` : ""}`, e?.message, e?.stack);
359
- }
360
- if (agentUsed &&
361
- agentUsed !== "code" &&
362
- agentUsed !== "plan" &&
363
- testGenConfig.testCase.name &&
364
- testGenConfig.options) {
365
- await new reporter_1.TestGenUpdatesReporter().reportGenAssets({
366
- projectRepoName: testGenConfig.options.metadata.projectRepoName,
367
- testName: testGenConfig.testCase.name,
332
+ (0, session_1.setSessionDetails)({
333
+ testCaseId: testGenConfig.testCase.id,
334
+ sessionId: testGenConfig.options?.metadata.testSessionId,
335
+ generationId: testGenConfig.options?.metadata.generationId,
336
+ projectRepoName: testGenConfig.options?.metadata.projectRepoName,
368
337
  });
369
- }
370
- removeListeners();
371
- await (0, llm_1.flushAllTraces)();
372
- await (0, logger_1.waitForLogsToFlush)();
373
- await (0, session_1.endSession)();
374
- if (testGenFailed) {
375
- process.exit(1);
376
- }
377
- else {
378
- process.exit(0);
379
- }
338
+ if (testGenConfig.build?.url) {
339
+ // Download the build if repo has a download script
340
+ await (0, test_build_1.downloadBuild)({
341
+ buildUrl: testGenConfig.build.url,
342
+ repoPath: process.cwd(),
343
+ apiKey: process.env.EMPIRICALRUN_API_KEY,
344
+ });
345
+ }
346
+ let agentUsed;
347
+ let testGenFailed = false;
348
+ try {
349
+ agentUsed = await runAgentsWorkflow(testGenConfig, testGenToken);
350
+ }
351
+ catch (e) {
352
+ testGenFailed = true;
353
+ new logger_1.CustomLogger().error(`Failed to generate test for the scenario. ${process.env.LOG_URL ? `[view log](${process.env.LOG_URL})` : ""}`, e?.message, e?.stack);
354
+ }
355
+ if (agentUsed &&
356
+ agentUsed !== "code" &&
357
+ agentUsed !== "plan" &&
358
+ testGenConfig.testCase.name &&
359
+ testGenConfig.options) {
360
+ await new reporter_1.TestGenUpdatesReporter().reportGenAssets({
361
+ projectRepoName: testGenConfig.options.metadata.projectRepoName,
362
+ testName: testGenConfig.testCase.name,
363
+ });
364
+ }
365
+ await (0, llm_1.flushAllTraces)();
366
+ await (0, logger_1.waitForLogsToFlush)();
367
+ await (0, session_1.endSession)();
368
+ if (testGenFailed) {
369
+ process.exit(1);
370
+ }
371
+ else {
372
+ process.exit(0);
373
+ }
374
+ });
375
+ program.parse(process.argv);
380
376
  }
381
377
  main().catch((error) => {
382
378
  console.error("Unhandled error in main function:", error);
@@ -0,0 +1,4 @@
1
+ export declare function runSetup({ repoName }: {
2
+ repoName: string;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/bin/setup.ts"],"names":[],"mappings":"AA+EA,wBAAsB,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,iBA2BhE"}
@@ -0,0 +1,86 @@
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.runSetup = runSetup;
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const util_1 = require("util");
11
+ const api_client_1 = require("../auth/api-client");
12
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
13
+ async function cloneRepository(cloneUrl, repoName) {
14
+ const targetDir = path_1.default.join(process.cwd(), repoName);
15
+ if (fs_1.default.existsSync(targetDir)) {
16
+ console.error(`Directory '${repoName}' already exists in current location`);
17
+ process.exit(1);
18
+ }
19
+ console.log(`📥 Cloning repository to ./${repoName}...`);
20
+ const { stderr } = await execAsync(`git clone ${cloneUrl} ${repoName}`, {
21
+ cwd: process.cwd(),
22
+ });
23
+ if (stderr && !stderr.includes("Cloning into")) {
24
+ console.error(`Git clone error: ${stderr}`);
25
+ process.exit(1);
26
+ }
27
+ console.log(`✅ Repository cloned successfully to ./${repoName}`);
28
+ return targetDir;
29
+ }
30
+ async function installDependencies(targetDir) {
31
+ console.log(`📦 Installing dependencies...`);
32
+ try {
33
+ const { stderr: installStderr } = await execAsync(`npm install`, {
34
+ cwd: targetDir,
35
+ });
36
+ if (installStderr && !installStderr.toLowerCase().includes("npm warn")) {
37
+ console.error(`npm install error: ${installStderr}`);
38
+ process.exit(1);
39
+ }
40
+ console.log(`✅ Dependencies installed successfully`);
41
+ }
42
+ catch (installError) {
43
+ console.error(`❌ Failed to install dependencies: ${installError.message}`);
44
+ process.exit(1);
45
+ }
46
+ }
47
+ async function installPlaywrightBrowser(targetDir) {
48
+ console.log(`🎭 Installing Playwright browser...`);
49
+ try {
50
+ const { stderr: playwrightStderr } = await execAsync(`npx playwright install chromium --with-deps`, {
51
+ cwd: targetDir,
52
+ });
53
+ if (playwrightStderr && !playwrightStderr.includes("Installing")) {
54
+ console.error(`Playwright install error: ${playwrightStderr}`);
55
+ process.exit(1);
56
+ }
57
+ console.log(`✅ Playwright browser installed successfully`);
58
+ }
59
+ catch (playwrightError) {
60
+ console.error(`❌ Failed to install Playwright browser: ${playwrightError.message}`);
61
+ process.exit(1);
62
+ }
63
+ }
64
+ async function runSetup({ repoName }) {
65
+ try {
66
+ console.log(`🔍 Fetching details for repository: ${repoName}`);
67
+ const response = await api_client_1.apiClient.request(`/api/projects/clone?project_repo_name=${repoName}`);
68
+ if (!response.ok) {
69
+ if (response.status === 404) {
70
+ throw new Error(`Repository '${repoName}' not found in your projects`);
71
+ }
72
+ throw new Error(`Failed to fetch repository details: ${response.statusText}`);
73
+ }
74
+ const { data: repoDetails } = await response.json();
75
+ const { clone_url } = repoDetails;
76
+ console.log(`📂 Clone URL: ${clone_url}`);
77
+ const targetDir = await cloneRepository(clone_url, repoName);
78
+ await installDependencies(targetDir);
79
+ await installPlaywrightBrowser(targetDir);
80
+ console.log(`\n🎉 Setup complete! Repository is ready at ./${repoName}`);
81
+ }
82
+ catch (error) {
83
+ console.error(`❌ Setup failed: ${error.message}`);
84
+ process.exit(1);
85
+ }
86
+ }
@@ -6,13 +6,8 @@ export interface CLIOptions {
6
6
  file?: string;
7
7
  prompt?: string;
8
8
  suites?: string;
9
- useChat?: boolean;
10
- useDiskForChatState?: boolean;
11
- initialPrompt?: string;
12
- chatSessionId?: string;
13
- chatModel?: (typeof ARGS_TO_MODEL_MAP)[keyof typeof ARGS_TO_MODEL_MAP];
14
- useRecorder?: boolean;
9
+ repoName?: string;
15
10
  }
16
- export declare function validateAndCompleteCliOptions(options: CLIOptions): Promise<CLIOptions>;
11
+ export declare function validateAndCompleteCliOptions(options: CLIOptions, requiredFields?: string[]): Promise<CLIOptions>;
17
12
  export declare function printBanner(): Promise<void>;
18
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAKjE,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAgBjE,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;IAGvE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CA8DrB;AAeD,wBAAsB,WAAW,kBAgDhC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAKjE,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAgBjE,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,EACnB,cAAc,GAAE,MAAM,EAA+B,GACpD,OAAO,CAAC,UAAU,CAAC,CAqDrB;AAeD,wBAAsB,WAAW,kBAgDhC"}
@@ -22,20 +22,12 @@ exports.ARGS_TO_MODEL_MAP = {
22
22
  "o4-mini": "o4-mini-2025-04-16",
23
23
  "o4-mini-2025-04-16": "o4-mini-2025-04-16",
24
24
  };
25
- async function validateAndCompleteCliOptions(options) {
25
+ async function validateAndCompleteCliOptions(options, requiredFields = ["name", "file", "prompt"]) {
26
26
  // For existing flow between dashboard <> test-gen (via ci-worker)
27
27
  const hasToken = !!options.token;
28
28
  if (hasToken) {
29
29
  return options;
30
30
  }
31
- let requiredFields = ["name", "file", "prompt"];
32
- if (options.useChat) {
33
- // Chat agent can prompt the user directly, nothing is required in CLI args
34
- requiredFields = [];
35
- }
36
- if (options.useRecorder) {
37
- requiredFields = ["name"];
38
- }
39
31
  const questions = [];
40
32
  if (!options.name && requiredFields.includes("name")) {
41
33
  questions.push({
@@ -45,7 +45,10 @@ class FileServiceServer {
45
45
  app.post("/codegen-sources", async (req) => {
46
46
  const payload = req.body;
47
47
  this.codegenSources = payload.map((c) => c.actions.join("\n")).join("\n");
48
- console.log("[FileServiceServer] Received codegen sources", this.codegenSources);
48
+ // console.log(
49
+ // "[FileServiceServer] Received codegen sources",
50
+ // this.codegenSources,
51
+ // );
49
52
  });
50
53
  app.post("/agent-results", async (req, res) => {
51
54
  const { generatedCode, importPaths, result, usage } = req.body;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAahD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAsB7C,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,GAAG,YAAY,EAC5B,KAAK,CAAC,EAAE,SAAS,iBAwElB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,IAAI,iBAY7C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAchD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAsB7C,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,GAAG,YAAY,EAC5B,KAAK,CAAC,EAAE,SAAS,iBAwElB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,IAAI,iBAY7C"}
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ const for_recorder_1 = require("./agent/cua/pw-codegen/pw-pause/for-recorder");
13
13
  const run_1 = require("./agent/master/run");
14
14
  const scenarios_1 = require("./bin/utils/scenarios");
15
15
  const client_1 = __importDefault(require("./file/client"));
16
+ const logger_1 = require("./logger");
16
17
  const reporter_1 = require("./reporter");
17
18
  const session_1 = require("./session");
18
19
  var test_build_1 = require("./test-build");
@@ -104,7 +105,7 @@ async function recordTest(pageRef) {
104
105
  if (!canUsePwPause) {
105
106
  return;
106
107
  }
107
- console.log("[getCodegen] using PlaywrightPauseCodegen");
108
+ logger_1.logger.debug("[getCodegen] using PlaywrightPauseCodegen");
108
109
  const codegen = new for_recorder_1.PlaywrightPauseCodegenForRecorder(async (sources) => {
109
110
  await fileServiceClient.sendCodegenSources(sources);
110
111
  });
@@ -0,0 +1,3 @@
1
+ import Logger from "console-log-level";
2
+ export declare const logger: Logger.Logger;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,MAAyB,MAAM,mBAAmB,CAAC;AAE1D,eAAO,MAAM,MAAM,eAGjB,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,11 @@
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.logger = void 0;
7
+ const console_log_level_1 = __importDefault(require("console-log-level"));
8
+ exports.logger = (0, console_log_level_1.default)({
9
+ prefix: "test-gen",
10
+ level: process.env.LOG_LEVEL || "info",
11
+ });
@@ -0,0 +1,2 @@
1
+ export declare function fetchEnvironmentVariables(repoName: string): Promise<Record<string, string>>;
2
+ //# sourceMappingURL=env-variables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-variables.d.ts","sourceRoot":"","sources":["../../src/recorder/env-variables.ts"],"names":[],"mappings":"AAEA,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgCjC"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchEnvironmentVariables = fetchEnvironmentVariables;
4
+ const api_client_1 = require("../auth/api-client");
5
+ async function fetchEnvironmentVariables(repoName) {
6
+ try {
7
+ const response = await api_client_1.apiClient.request(`/api/environment-variables?project_repo_name=${encodeURIComponent(repoName)}`, {
8
+ method: "GET",
9
+ });
10
+ if (!response.ok) {
11
+ const errorMessage = await response.text();
12
+ throw new Error(`Failed to fetch environment variables: ${errorMessage}`);
13
+ }
14
+ const data = await response.json();
15
+ if (!data.data) {
16
+ console.error("Failed to fetch environment variables:", data);
17
+ throw new Error("Failed to fetch environment variables");
18
+ }
19
+ const envVars = data.data.environment_variables.reduce((acc, envVar) => {
20
+ acc[envVar.name] = envVar.value;
21
+ return acc;
22
+ }, {});
23
+ return envVars;
24
+ }
25
+ catch (error) {
26
+ console.error("Failed to fetch environment variables:", error);
27
+ return {};
28
+ }
29
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recorder/index.ts"],"names":[],"mappings":"AAyCA,wBAAsB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,iBAiF3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recorder/index.ts"],"names":[],"mappings":"AAkBA,wBAAsB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,iBAyF3D"}
@@ -6,66 +6,56 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runRecorder = runRecorder;
7
7
  const test_run_1 = require("@empiricalrun/test-run");
8
8
  const detect_port_1 = __importDefault(require("detect-port"));
9
- const fs_1 = __importDefault(require("fs"));
10
9
  const path_1 = __importDefault(require("path"));
11
10
  const utils_1 = require("../agent/browsing/utils");
12
- const chat_1 = require("../agent/chat");
13
11
  const pw_pause_1 = require("../agent/cua/pw-codegen/pw-pause");
14
- const utils_2 = require("../artifacts/utils");
15
12
  const server_1 = require("../file/server");
13
+ const logger_1 = require("../logger");
16
14
  const display_1 = require("./display");
15
+ const env_variables_1 = require("./env-variables");
17
16
  const request_1 = require("./request");
18
17
  const temp_files_1 = require("./temp-files");
19
18
  const upload_1 = require("./upload");
20
19
  const validation_1 = require("./validation");
21
- function extractVideoAttachments(repoDir) {
22
- try {
23
- const summaryPath = path_1.default.join(repoDir, "summary.json");
24
- if (!fs_1.default.existsSync(summaryPath)) {
25
- console.log("summary.json not found");
26
- return [];
27
- }
28
- const summaryContent = JSON.parse(fs_1.default.readFileSync(summaryPath, "utf-8"));
29
- const attachments = (0, utils_2.extractAttachmentsFromPlaywrightJSONReport)(summaryContent, "temp test");
30
- const videoPaths = attachments
31
- .filter((attachment) => attachment.contentType === "video/webm")
32
- .map((attachment) => attachment.path);
33
- return videoPaths;
34
- }
35
- catch (error) {
36
- console.warn("Error processing summary.json:", error);
37
- return [];
38
- }
39
- }
40
20
  async function runRecorder({ name }) {
41
21
  console.log(`Recording for test name: ${name}`);
42
22
  const repoDir = process.cwd();
23
+ let repoName = "";
24
+ let fileServer = null;
25
+ process.on("SIGINT", async () => {
26
+ try {
27
+ await (0, temp_files_1.deleteTempTestFile)();
28
+ await (0, pw_pause_1.revertToOriginalPwCode)(repoDir);
29
+ if (fileServer) {
30
+ await fileServer.stop();
31
+ }
32
+ }
33
+ catch (error) {
34
+ console.error("Error during cleanup:", error);
35
+ }
36
+ process.exit(0);
37
+ });
43
38
  try {
44
- await (0, validation_1.validate)(repoDir);
39
+ repoName = await (0, validation_1.validatePackageJson)(repoDir);
45
40
  }
46
41
  catch (error) {
47
42
  console.error("Error running recorder:", error);
48
43
  process.exit(1);
49
44
  }
50
- if (!process.env.EMPIRICALRUN_API_KEY) {
51
- console.error("EMPIRICALRUN_API_KEY is not set. Please set it in your environment variables.");
52
- process.exit(1);
53
- }
54
45
  try {
55
- // Prepare playwright for codegen
56
- console.log("[generateTestWithBrowserAgent] Preparing playwright for codegen");
46
+ logger_1.logger.debug("[generateTestWithBrowserAgent] Preparing playwright for codegen");
57
47
  await (0, pw_pause_1.preparePlaywrightForCodegen)(repoDir);
58
48
  }
59
49
  catch (err) {
60
- console.warn("[generateTestWithBrowserAgent] Error preparing playwright for codegen", err);
50
+ logger_1.logger.warn("[generateTestWithBrowserAgent] Error preparing playwright for codegen", err);
61
51
  }
62
- const envVariables = await (0, chat_1.fetchEnvironmentVariables)();
52
+ const envVariables = await (0, env_variables_1.fetchEnvironmentVariables)(repoName);
63
53
  await (0, temp_files_1.createTempTestFile)();
64
54
  const absFilePath = path_1.default.join(process.cwd(), "tests", "temp-test.spec.ts");
65
55
  await (0, utils_1.addImportForMethod)(absFilePath, "recordTest");
66
56
  // Start a file service for IPC with the agent (which runs in a different process)
67
57
  const availablePort = await (0, detect_port_1.default)(3030);
68
- const fileServer = new server_1.FileServiceServer({
58
+ fileServer = new server_1.FileServiceServer({
69
59
  port: availablePort,
70
60
  repoDir,
71
61
  updateFile: false,
@@ -84,10 +74,10 @@ async function runRecorder({ name }) {
84
74
  IPC_FILE_SERVICE_PORT: availablePort.toString(),
85
75
  },
86
76
  });
87
- const videoPaths = extractVideoAttachments(repoDir);
77
+ const videoPaths = (0, upload_1.extractVideoAttachments)(repoDir);
88
78
  let attachments = [];
89
79
  if (videoPaths.length === 0) {
90
- console.warn("No video attachments found for temp test");
80
+ logger_1.logger.warn("No video attachments found for temp test");
91
81
  }
92
82
  else {
93
83
  const videoUrls = await (0, upload_1.uploadVideosWithSpinner)(videoPaths, name);
@@ -101,6 +91,7 @@ async function runRecorder({ name }) {
101
91
  await fileServer.stop();
102
92
  const finalCode = await (0, display_1.displayResultsAndConfirm)(name, codegenResult);
103
93
  await (0, request_1.sendToDashboardAsRequest)({
94
+ repoName,
104
95
  testName: name,
105
96
  codegenResult: finalCode,
106
97
  attachments,
@@ -1,4 +1,5 @@
1
- export declare function sendToDashboardAsRequest({ testName, codegenResult, attachments, }: {
1
+ export declare function sendToDashboardAsRequest({ repoName, testName, codegenResult, attachments, }: {
2
+ repoName: string;
2
3
  testName: string;
3
4
  codegenResult: string;
4
5
  attachments: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/recorder/request.ts"],"names":[],"mappings":"AAeA,wBAAsB,wBAAwB,CAAC,EAC7C,QAAQ,EACR,aAAa,EACb,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,iBAKA"}
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/recorder/request.ts"],"names":[],"mappings":"AAaA,wBAAsB,wBAAwB,CAAC,EAC7C,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,iBAMA"}