@dyyz1993/agent-browser 0.13.2 → 0.24.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 (164) hide show
  1. package/README.md +108 -0
  2. package/bin/agent-browser-darwin-arm64 +0 -0
  3. package/bin/agent-browser-darwin-x64 +0 -0
  4. package/bin/agent-browser-linux-arm64 +0 -0
  5. package/bin/agent-browser-linux-x64 +0 -0
  6. package/bin/agent-browser-win32-x64.exe +0 -0
  7. package/dist/__tests__/e2e/utils/test-helpers.d.ts +1 -0
  8. package/dist/__tests__/e2e/utils/test-helpers.d.ts.map +1 -1
  9. package/dist/__tests__/e2e/utils/test-helpers.js +14 -1
  10. package/dist/__tests__/e2e/utils/test-helpers.js.map +1 -1
  11. package/dist/__tests__/utils/free-port.d.ts +2 -0
  12. package/dist/__tests__/utils/free-port.d.ts.map +1 -0
  13. package/dist/__tests__/utils/free-port.js +18 -0
  14. package/dist/__tests__/utils/free-port.js.map +1 -0
  15. package/dist/__tests__/utils/parseCli.d.ts.map +1 -1
  16. package/dist/__tests__/utils/parseCli.js +83 -9
  17. package/dist/__tests__/utils/parseCli.js.map +1 -1
  18. package/dist/actions.d.ts.map +1 -1
  19. package/dist/actions.js +298 -9
  20. package/dist/actions.js.map +1 -1
  21. package/dist/browser.d.ts +11 -1
  22. package/dist/browser.d.ts.map +1 -1
  23. package/dist/browser.js +75 -19
  24. package/dist/browser.js.map +1 -1
  25. package/dist/cli/commands.d.ts.map +1 -1
  26. package/dist/cli/commands.js +172 -15
  27. package/dist/cli/commands.js.map +1 -1
  28. package/dist/cli/connection.d.ts +13 -0
  29. package/dist/cli/connection.d.ts.map +1 -1
  30. package/dist/cli/connection.js +137 -48
  31. package/dist/cli/connection.js.map +1 -1
  32. package/dist/cli/flags.d.ts.map +1 -1
  33. package/dist/cli/flags.js +0 -1
  34. package/dist/cli/flags.js.map +1 -1
  35. package/dist/cli/help.d.ts.map +1 -1
  36. package/dist/cli/help.js +63 -22
  37. package/dist/cli/help.js.map +1 -1
  38. package/dist/cli/output.d.ts.map +1 -1
  39. package/dist/cli/output.js +0 -32
  40. package/dist/cli/output.js.map +1 -1
  41. package/dist/cli.js +20 -2
  42. package/dist/cli.js.map +1 -1
  43. package/dist/daemon.d.ts +1 -0
  44. package/dist/daemon.d.ts.map +1 -1
  45. package/dist/daemon.js +291 -264
  46. package/dist/daemon.js.map +1 -1
  47. package/dist/diff.d.ts.map +1 -1
  48. package/dist/diff.js +1 -1
  49. package/dist/diff.js.map +1 -1
  50. package/dist/flow/exporters/cypress.d.ts +9 -0
  51. package/dist/flow/exporters/cypress.d.ts.map +1 -0
  52. package/dist/flow/exporters/cypress.js +256 -0
  53. package/dist/flow/exporters/cypress.js.map +1 -0
  54. package/dist/flow/exporters/index.d.ts +6 -0
  55. package/dist/flow/exporters/index.d.ts.map +1 -0
  56. package/dist/flow/exporters/index.js +5 -0
  57. package/dist/flow/exporters/index.js.map +1 -0
  58. package/dist/flow/exporters/playwright.d.ts +20 -0
  59. package/dist/flow/exporters/playwright.d.ts.map +1 -0
  60. package/dist/flow/exporters/playwright.js +175 -0
  61. package/dist/flow/exporters/playwright.js.map +1 -0
  62. package/dist/flow/exporters/python.d.ts +20 -0
  63. package/dist/flow/exporters/python.d.ts.map +1 -0
  64. package/dist/flow/exporters/python.js +163 -0
  65. package/dist/flow/exporters/python.js.map +1 -0
  66. package/dist/flow/exporters/selenium.d.ts +9 -0
  67. package/dist/flow/exporters/selenium.d.ts.map +1 -0
  68. package/dist/flow/exporters/selenium.js +298 -0
  69. package/dist/flow/exporters/selenium.js.map +1 -0
  70. package/dist/flow/exporters/types.d.ts +13 -0
  71. package/dist/flow/exporters/types.d.ts.map +1 -0
  72. package/dist/flow/exporters/types.js +2 -0
  73. package/dist/flow/exporters/types.js.map +1 -0
  74. package/dist/flow/flow-executor.d.ts +57 -0
  75. package/dist/flow/flow-executor.d.ts.map +1 -0
  76. package/dist/flow/flow-executor.js +1263 -0
  77. package/dist/flow/flow-executor.js.map +1 -0
  78. package/dist/flow/index.d.ts +15 -0
  79. package/dist/flow/index.d.ts.map +1 -0
  80. package/dist/flow/index.js +10 -0
  81. package/dist/flow/index.js.map +1 -0
  82. package/dist/flow/output.d.ts +11 -0
  83. package/dist/flow/output.d.ts.map +1 -0
  84. package/dist/flow/output.js +84 -0
  85. package/dist/flow/output.js.map +1 -0
  86. package/dist/flow/plugin-system.d.ts +48 -0
  87. package/dist/flow/plugin-system.d.ts.map +1 -0
  88. package/dist/flow/plugin-system.js +132 -0
  89. package/dist/flow/plugin-system.js.map +1 -0
  90. package/dist/flow/plugins/file-output-plugin.d.ts +8 -0
  91. package/dist/flow/plugins/file-output-plugin.d.ts.map +1 -0
  92. package/dist/flow/plugins/file-output-plugin.js +31 -0
  93. package/dist/flow/plugins/file-output-plugin.js.map +1 -0
  94. package/dist/flow/plugins/index.d.ts +4 -0
  95. package/dist/flow/plugins/index.d.ts.map +1 -0
  96. package/dist/flow/plugins/index.js +4 -0
  97. package/dist/flow/plugins/index.js.map +1 -0
  98. package/dist/flow/plugins/logging-plugin.d.ts +7 -0
  99. package/dist/flow/plugins/logging-plugin.d.ts.map +1 -0
  100. package/dist/flow/plugins/logging-plugin.js +40 -0
  101. package/dist/flow/plugins/logging-plugin.js.map +1 -0
  102. package/dist/flow/plugins/webhook-plugin.d.ts +7 -0
  103. package/dist/flow/plugins/webhook-plugin.d.ts.map +1 -0
  104. package/dist/flow/plugins/webhook-plugin.js +24 -0
  105. package/dist/flow/plugins/webhook-plugin.js.map +1 -0
  106. package/dist/flow/presets/index.d.ts +10 -0
  107. package/dist/flow/presets/index.d.ts.map +1 -0
  108. package/dist/flow/presets/index.js +29 -0
  109. package/dist/flow/presets/index.js.map +1 -0
  110. package/dist/flow/recorder-to-flow.d.ts +70 -0
  111. package/dist/flow/recorder-to-flow.d.ts.map +1 -0
  112. package/dist/flow/recorder-to-flow.js +392 -0
  113. package/dist/flow/recorder-to-flow.js.map +1 -0
  114. package/dist/flow/site-manager.d.ts +24 -0
  115. package/dist/flow/site-manager.d.ts.map +1 -0
  116. package/dist/flow/site-manager.js +125 -0
  117. package/dist/flow/site-manager.js.map +1 -0
  118. package/dist/flow/types.d.ts +196 -0
  119. package/dist/flow/types.d.ts.map +1 -0
  120. package/dist/flow/types.js +2 -0
  121. package/dist/flow/types.js.map +1 -0
  122. package/dist/flow/yaml-parser.d.ts +15 -0
  123. package/dist/flow/yaml-parser.d.ts.map +1 -0
  124. package/dist/flow/yaml-parser.js +216 -0
  125. package/dist/flow/yaml-parser.js.map +1 -0
  126. package/dist/human-mouse.d.ts.map +1 -1
  127. package/dist/protocol.d.ts.map +1 -1
  128. package/dist/protocol.js +15 -11
  129. package/dist/protocol.js.map +1 -1
  130. package/dist/rc-config.d.ts.map +1 -1
  131. package/dist/rc-config.js +1 -2
  132. package/dist/rc-config.js.map +1 -1
  133. package/dist/recorder/inject.js +730 -332
  134. package/dist/snapshot-store.d.ts +83 -0
  135. package/dist/snapshot-store.d.ts.map +1 -0
  136. package/dist/snapshot-store.js +112 -0
  137. package/dist/snapshot-store.js.map +1 -0
  138. package/dist/snapshot.d.ts +6 -7
  139. package/dist/snapshot.d.ts.map +1 -1
  140. package/dist/snapshot.js +471 -17
  141. package/dist/snapshot.js.map +1 -1
  142. package/dist/stream-server-standalone.d.ts.map +1 -1
  143. package/dist/stream-server-standalone.js.map +1 -1
  144. package/dist/stream-server.d.ts.map +1 -1
  145. package/dist/stream-server.js +38 -13
  146. package/dist/stream-server.js.map +1 -1
  147. package/dist/test-live.js +5 -5
  148. package/dist/test-live.js.map +1 -1
  149. package/dist/types.d.ts +13 -9
  150. package/dist/types.d.ts.map +1 -1
  151. package/dist/types.js.map +1 -1
  152. package/dist/viewer-script.d.ts.map +1 -1
  153. package/dist/viewer-script.js +12 -6
  154. package/dist/viewer-script.js.map +1 -1
  155. package/package.json +18 -5
  156. package/skills/agent-browser/SKILL.md +151 -3
  157. package/dist/ios-actions.d.ts +0 -11
  158. package/dist/ios-actions.d.ts.map +0 -1
  159. package/dist/ios-actions.js +0 -228
  160. package/dist/ios-actions.js.map +0 -1
  161. package/dist/ios-manager.d.ts +0 -266
  162. package/dist/ios-manager.d.ts.map +0 -1
  163. package/dist/ios-manager.js +0 -1076
  164. package/dist/ios-manager.js.map +0 -1
package/dist/actions.js CHANGED
@@ -7,6 +7,10 @@ import { getViewerUrl, getViewerWsUrl, getViewerPort, getMessageBridgeUrl, getEx
7
7
  import { detectMainContent, generateContentTips } from './content-detection.js';
8
8
  import { humanClick, humanType, humanMoveTo, humanWander, getHumanConfigFromEnv, } from './human-mouse.js';
9
9
  import { successResponse, errorResponse } from './protocol.js';
10
+ import { parseYamlSiteFile, loadSitesFromDirectory, loadAllSites, findFlow, validateYamlFile, } from './flow/yaml-parser.js';
11
+ import { recorderToFlowFromFile, siteToYamlString } from './flow/recorder-to-flow.js';
12
+ import { FlowExecutor } from './flow/flow-executor.js';
13
+ import { PlaywrightExporter, PythonExporter, CypressExporter, SeleniumExporter, } from './flow/exporters/index.js';
10
14
  // Callback for screencast frames - will be set by the daemon when streaming is active
11
15
  let screencastFrameCallback = null;
12
16
  /**
@@ -98,6 +102,9 @@ export function toAIFriendlyError(error, selector) {
98
102
  */
99
103
  export async function executeCommand(command, browser) {
100
104
  try {
105
+ if (command.action === 'flow') {
106
+ return await handleFlowAction(command, browser);
107
+ }
101
108
  const cmd = command;
102
109
  switch (cmd.action) {
103
110
  case 'launch':
@@ -366,6 +373,12 @@ export async function executeCommand(command, browser) {
366
373
  return handleConfig(cmd);
367
374
  case 'history':
368
375
  return await handleHistory(cmd, browser);
376
+ case 'selector-for':
377
+ return await handleSelectorFor(cmd, browser);
378
+ case 'selectors-of':
379
+ return await handleSelectorsOf(cmd, browser);
380
+ case 'validate':
381
+ return await handleValidate(cmd, browser);
369
382
  default: {
370
383
  const unknownCommand = cmd;
371
384
  return errorResponse(unknownCommand.id, `Unknown action: ${unknownCommand.action}`);
@@ -661,10 +674,15 @@ async function handleEvaluate(command, browser) {
661
674
  try {
662
675
  let script;
663
676
  if (command.file) {
664
- if (!existsSync(command.file)) {
665
- throw new Error(`Script file not found: ${command.file}`);
677
+ const resolvedPath = path.resolve(command.file);
678
+ const cwd = process.cwd();
679
+ if (!resolvedPath.startsWith(cwd)) {
680
+ throw new Error(`Security: file path must be within project directory. Got: ${resolvedPath}`);
681
+ }
682
+ if (!existsSync(resolvedPath)) {
683
+ throw new Error(`Script file not found: ${resolvedPath}`);
666
684
  }
667
- script = readFileSync(command.file, 'utf-8');
685
+ script = readFileSync(resolvedPath, 'utf-8');
668
686
  }
669
687
  else if (command.script) {
670
688
  script = command.script;
@@ -765,7 +783,12 @@ async function handleScroll(command, browser) {
765
783
  break;
766
784
  }
767
785
  }
768
- await page.evaluate(`window.scrollBy(${deltaX}, ${deltaY})`);
786
+ const safeDeltaX = Number(deltaX) || 0;
787
+ const safeDeltaY = Number(deltaY) || 0;
788
+ await page.evaluate(({ dx, dy }) => window.scrollBy(dx, dy), {
789
+ dx: safeDeltaX,
790
+ dy: safeDeltaY,
791
+ });
769
792
  }
770
793
  return successResponse(command.id, { scrolled: true });
771
794
  }
@@ -1508,14 +1531,14 @@ async function handleStyles(command, browser) {
1508
1531
  if (browser.isRef(command.selector)) {
1509
1532
  const locator = browser.getLocator(command.selector);
1510
1533
  const element = (await locator.evaluate((el, script) => {
1511
- const fn = eval(script);
1534
+ const fn = new Function('return ' + script)();
1512
1535
  return fn(el);
1513
1536
  }, extractStylesScript));
1514
1537
  return successResponse(command.id, { elements: [element] });
1515
1538
  }
1516
1539
  // CSS selector - can match multiple elements
1517
1540
  const elements = (await frame.locator(command.selector).evaluateAll((els, script) => {
1518
- const fn = eval(script);
1541
+ const fn = new Function('return ' + script)();
1519
1542
  return els.map((el) => fn(el));
1520
1543
  }, extractStylesScript));
1521
1544
  return successResponse(command.id, { elements });
@@ -2182,14 +2205,23 @@ async function handleRecorderReplay(command, browser) {
2182
2205
  // Filter out env-only lines (keep them for env setup but not as commands)
2183
2206
  const envLines = cliCommands.filter((l) => l.startsWith('AGENT_BROWSER_'));
2184
2207
  const cmdLines = cliCommands.filter((l) => l.startsWith('agent-browser '));
2185
- // Set env vars from recording
2208
+ // Set env vars from recording (skip AGENT_BROWSER_HUMAN to avoid
2209
+ // human-mode coordinate issues during replay where elements may be
2210
+ // scrolled off-screen)
2186
2211
  const originalEnv = {};
2187
2212
  for (const envLine of envLines) {
2188
2213
  const eqIdx = envLine.indexOf('=');
2189
2214
  if (eqIdx > 0) {
2190
2215
  const key = envLine.substring(0, eqIdx);
2216
+ if (key === 'AGENT_BROWSER_HUMAN')
2217
+ continue;
2218
+ let value = envLine.substring(eqIdx + 1);
2219
+ const spaceIdx = value.indexOf(' ');
2220
+ if (spaceIdx > 0) {
2221
+ value = value.substring(0, spaceIdx);
2222
+ }
2191
2223
  originalEnv[key] = process.env[key];
2192
- process.env[key] = envLine.substring(eqIdx + 1);
2224
+ process.env[key] = value;
2193
2225
  }
2194
2226
  }
2195
2227
  function parseCommandLine(line) {
@@ -2316,7 +2348,6 @@ function handleConfig(command) {
2316
2348
  userAgent: process.env.AGENT_BROWSER_USER_AGENT || null,
2317
2349
  provider: process.env.AGENT_BROWSER_PROVIDER || null,
2318
2350
  allowFileAccess: process.env.AGENT_BROWSER_ALLOW_FILE_ACCESS === '1',
2319
- iosDevice: process.env.AGENT_BROWSER_IOS_DEVICE || null,
2320
2351
  streamPort: getViewerPort(),
2321
2352
  headed: process.env.AGENT_BROWSER_HEADED === '1',
2322
2353
  human: humanConfig,
@@ -2370,4 +2401,262 @@ async function handleHistory(command, browser) {
2370
2401
  const history = browser.getHistory(command.filter);
2371
2402
  return successResponse(command.id, { history });
2372
2403
  }
2404
+ async function handleSelectorFor(command, browser) {
2405
+ const store = browser.getSnapshotStore();
2406
+ const colonIndex = command.target.indexOf(':');
2407
+ if (colonIndex === -1) {
2408
+ return errorResponse(command.id, `Invalid target format: "${command.target}". Expected "snapshotId:refOrIndex" (e.g., "snap_3:@e1" or "snap_3:1").`);
2409
+ }
2410
+ const snapshotId = command.target.substring(0, colonIndex);
2411
+ const refOrIndex = command.target.substring(colonIndex + 1);
2412
+ await browser.ensureSelectorsGenerated(snapshotId);
2413
+ const element = store.getElement(snapshotId, refOrIndex);
2414
+ if (!element) {
2415
+ return errorResponse(command.id, `Element not found: "${refOrIndex}" in snapshot "${snapshotId}". Run 'snapshot' to get fresh snapshot data.`);
2416
+ }
2417
+ return successResponse(command.id, {
2418
+ snapshotId,
2419
+ ref: element.ref,
2420
+ index: element.index,
2421
+ role: element.role,
2422
+ name: element.name,
2423
+ cssSelector: element.cssSelector,
2424
+ xpath: element.xpath,
2425
+ });
2426
+ }
2427
+ async function handleSelectorsOf(command, browser) {
2428
+ const store = browser.getSnapshotStore();
2429
+ await browser.ensureSelectorsGenerated(command.target);
2430
+ const elements = store.getElements(command.target);
2431
+ if (!elements) {
2432
+ return errorResponse(command.id, `Snapshot "${command.target}" not found. Run 'snapshot' to create a new snapshot.`);
2433
+ }
2434
+ return successResponse(command.id, {
2435
+ snapshotId: command.target,
2436
+ elements: elements.map((el) => ({
2437
+ ref: el.ref,
2438
+ index: el.index,
2439
+ role: el.role,
2440
+ name: el.name,
2441
+ cssSelector: el.cssSelector,
2442
+ xpath: el.xpath,
2443
+ })),
2444
+ });
2445
+ }
2446
+ async function handleValidate(command, browser) {
2447
+ const store = browser.getSnapshotStore();
2448
+ await browser.ensureSelectorsGenerated(command.target);
2449
+ const elements = store.getElements(command.target);
2450
+ if (!elements) {
2451
+ return errorResponse(command.id, `Snapshot "${command.target}" not found. Run 'snapshot' to create a new snapshot.`);
2452
+ }
2453
+ const page = browser.getPage();
2454
+ const selectors = elements.map((el) => el.cssSelector);
2455
+ const matchCounts = await page.evaluate((sels) => {
2456
+ return sels.map((sel) => {
2457
+ try {
2458
+ return document.querySelectorAll(sel).length;
2459
+ }
2460
+ catch {
2461
+ return -1;
2462
+ }
2463
+ });
2464
+ }, selectors);
2465
+ const results = elements.map((el, i) => {
2466
+ const matchCount = matchCounts[i];
2467
+ let status;
2468
+ if (matchCount === -1) {
2469
+ status = 'invalid_selector';
2470
+ }
2471
+ else if (matchCount === 0) {
2472
+ status = 'not_found';
2473
+ }
2474
+ else if (matchCount === 1) {
2475
+ status = 'valid';
2476
+ }
2477
+ else {
2478
+ status = 'ambiguous';
2479
+ }
2480
+ return {
2481
+ ref: el.ref,
2482
+ index: el.index,
2483
+ cssSelector: el.cssSelector,
2484
+ status,
2485
+ matchCount,
2486
+ };
2487
+ });
2488
+ const failedCount = results.filter((r) => r.status === 'not_found' || r.status === 'invalid_selector').length;
2489
+ let newSnapshotId;
2490
+ if (failedCount > 0) {
2491
+ const newSnapshot = await browser.getSnapshot({ interactive: true });
2492
+ newSnapshotId = newSnapshot.snapshotId;
2493
+ }
2494
+ return successResponse(command.id, {
2495
+ snapshotId: command.target,
2496
+ results,
2497
+ newSnapshotId,
2498
+ });
2499
+ }
2500
+ async function handleFlowAction(command, browser) {
2501
+ const cmd = command;
2502
+ const subcommand = cmd.subcommand;
2503
+ switch (subcommand) {
2504
+ case 'run':
2505
+ return await handleFlowRun(cmd, browser);
2506
+ case 'list':
2507
+ return handleFlowList(cmd);
2508
+ case 'show':
2509
+ return handleFlowShow(cmd);
2510
+ case 'validate':
2511
+ return handleFlowValidate(cmd);
2512
+ case 'from-recorder':
2513
+ return handleFlowFromRecorder(cmd);
2514
+ case 'export':
2515
+ return handleFlowExport(cmd);
2516
+ default:
2517
+ return errorResponse(command.id, `Unknown flow subcommand: ${subcommand}`);
2518
+ }
2519
+ }
2520
+ function handleFlowList(command) {
2521
+ const sites = command.sitesDir ? loadSitesFromDirectory(command.sitesDir) : loadAllSites();
2522
+ const siteList = [];
2523
+ for (const [name, site] of sites) {
2524
+ siteList.push({
2525
+ name,
2526
+ description: site.description,
2527
+ flows: Object.keys(site.flows),
2528
+ });
2529
+ }
2530
+ return successResponse(command.id, { sites: siteList });
2531
+ }
2532
+ function handleFlowShow(command) {
2533
+ const sites = command.sitesDir ? loadSitesFromDirectory(command.sitesDir) : loadAllSites();
2534
+ const ref = command.siteFlow || '';
2535
+ const result = findFlow(sites, ref);
2536
+ if (!result) {
2537
+ return errorResponse(command.id, `Flow "${ref}" not found`);
2538
+ }
2539
+ return successResponse(command.id, {
2540
+ site: {
2541
+ name: result.site.name,
2542
+ description: result.site.description,
2543
+ baseUrl: result.site.baseUrl,
2544
+ },
2545
+ flow: result.flow,
2546
+ });
2547
+ }
2548
+ function handleFlowValidate(command) {
2549
+ const filePath = command.filePath || '';
2550
+ if (!filePath) {
2551
+ return errorResponse(command.id, 'Missing file path for validate');
2552
+ }
2553
+ const result = validateYamlFile(filePath);
2554
+ return successResponse(command.id, result);
2555
+ }
2556
+ function handleFlowFromRecorder(command) {
2557
+ const recorderFile = command.recorderFile;
2558
+ if (!recorderFile) {
2559
+ return errorResponse(command.id, 'Missing recorder YAML file path');
2560
+ }
2561
+ try {
2562
+ const result = recorderToFlowFromFile(recorderFile, {
2563
+ flowId: command.flowId,
2564
+ description: command.description,
2565
+ baseUrl: command.baseUrl,
2566
+ siteName: command.siteName,
2567
+ maxPaginateIterations: command.maxPaginateIterations,
2568
+ });
2569
+ const yamlString = siteToYamlString(result.site);
2570
+ if (command.outputFile) {
2571
+ writeFileSync(path.resolve(command.outputFile), yamlString, 'utf-8');
2572
+ return successResponse(command.id, {
2573
+ siteName: result.site.name,
2574
+ flowId: Object.keys(result.site.flows)[0],
2575
+ outputFile: command.outputFile,
2576
+ warnings: result.warnings,
2577
+ });
2578
+ }
2579
+ return successResponse(command.id, {
2580
+ siteName: result.site.name,
2581
+ flowId: Object.keys(result.site.flows)[0],
2582
+ yaml: yamlString,
2583
+ warnings: result.warnings,
2584
+ });
2585
+ }
2586
+ catch (e) {
2587
+ return errorResponse(command.id, `Failed to convert recorder YAML: ${e instanceof Error ? e.message : String(e)}`);
2588
+ }
2589
+ }
2590
+ async function handleFlowRun(command, browser) {
2591
+ const ref = command.siteFlow || '';
2592
+ const sites = command.sitesDir ? loadSitesFromDirectory(command.sitesDir) : loadAllSites();
2593
+ const result = findFlow(sites, ref);
2594
+ if (!result) {
2595
+ return errorResponse(command.id, `Flow "${ref}" not found. Available sites: ${[...sites.keys()].join(', ')}`);
2596
+ }
2597
+ const typedParams = {};
2598
+ if (result.flow.params) {
2599
+ for (const param of result.flow.params) {
2600
+ const raw = command.params?.[param.name];
2601
+ if (raw !== undefined) {
2602
+ switch (param.type) {
2603
+ case 'number':
2604
+ typedParams[param.name] = Number(raw);
2605
+ break;
2606
+ case 'boolean':
2607
+ typedParams[param.name] = raw === 'true' || raw === '1';
2608
+ break;
2609
+ default:
2610
+ typedParams[param.name] = raw;
2611
+ }
2612
+ }
2613
+ }
2614
+ }
2615
+ const executor = new FlowExecutor(browser);
2616
+ const flowResult = await executor.execute(result.site, result.flowName, typedParams);
2617
+ return successResponse(command.id, flowResult);
2618
+ }
2619
+ function handleFlowExport(command) {
2620
+ const filePath = command.filePath;
2621
+ if (!filePath) {
2622
+ return errorResponse(command.id, 'Missing file path for export');
2623
+ }
2624
+ let site;
2625
+ try {
2626
+ site = parseYamlSiteFile(filePath);
2627
+ }
2628
+ catch (e) {
2629
+ return errorResponse(command.id, `Failed to parse YAML file: ${e instanceof Error ? e.message : String(e)}`);
2630
+ }
2631
+ const flowEntries = Object.entries(site.flows);
2632
+ if (flowEntries.length === 0) {
2633
+ return errorResponse(command.id, 'No flows found in YAML file');
2634
+ }
2635
+ const flow = flowEntries[0][1];
2636
+ const format = command.format || 'playwright';
2637
+ const exporterMap = {
2638
+ playwright: new PlaywrightExporter(),
2639
+ python: new PythonExporter(),
2640
+ cypress: new CypressExporter(),
2641
+ selenium: new SeleniumExporter(),
2642
+ };
2643
+ const exporter = exporterMap[format];
2644
+ if (!exporter) {
2645
+ return errorResponse(command.id, `Unknown export format: "${format}". Available: ${Object.keys(exporterMap).join(', ')}`);
2646
+ }
2647
+ try {
2648
+ const script = exporter.export(flow.steps, {
2649
+ baseUrl: command.baseUrl || site.baseUrl,
2650
+ headless: command.headless,
2651
+ });
2652
+ return successResponse(command.id, {
2653
+ format: exporter.format,
2654
+ extension: exporter.extension,
2655
+ script,
2656
+ });
2657
+ }
2658
+ catch (e) {
2659
+ return errorResponse(command.id, `Export failed: ${e instanceof Error ? e.message : String(e)}`);
2660
+ }
2661
+ }
2373
2662
  //# sourceMappingURL=actions.js.map