@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/browser.js CHANGED
@@ -4,7 +4,8 @@ import os from 'node:os';
4
4
  import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync, statSync } from 'node:fs';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- import { getEnhancedSnapshot, parseRef } from './snapshot.js';
7
+ import { getEnhancedSnapshot, generateStableSelectors, parseRef, } from './snapshot.js';
8
+ import { SnapshotStore } from './snapshot-store.js';
8
9
  import { getEventCallbacks } from './actions.js';
9
10
  /**
10
11
  * Manages the Playwright browser lifecycle with multiple tabs/windows
@@ -46,6 +47,7 @@ export class BrowserManager {
46
47
  isRecordingHar = false;
47
48
  refMap = {};
48
49
  lastSnapshot = '';
50
+ snapshotStore = new SnapshotStore();
49
51
  scopedHeaderRoutes = new Map();
50
52
  commandHistory = [];
51
53
  // CDP session for screencast and input injection
@@ -93,7 +95,63 @@ export class BrowserManager {
93
95
  const snapshot = await getEnhancedSnapshot(frame, options);
94
96
  this.refMap = snapshot.refs;
95
97
  this.lastSnapshot = snapshot.tree;
96
- return snapshot;
98
+ let snapshotId;
99
+ const url = this.pages.length > 0 ? this.getPage().url() : '';
100
+ const elements = [];
101
+ let index = 1;
102
+ for (const [ref, data] of Object.entries(snapshot.refs)) {
103
+ elements.push({
104
+ ref,
105
+ index: index,
106
+ role: data.role,
107
+ name: data.name,
108
+ cssSelector: '',
109
+ xpath: '',
110
+ });
111
+ index++;
112
+ }
113
+ snapshotId = this.snapshotStore.create(url, elements, options?.framePath);
114
+ const elementCount = elements.length;
115
+ const header = `Snapshot #${snapshotId} (${elementCount} interactive elements)\n---`;
116
+ const tips = `---\nTips:\n Get selector: snapshot --selector-for ${snapshotId}:@e1\n Or by index: snapshot --selector-for ${snapshotId}:1\n List all: snapshot --selectors-of ${snapshotId}\n Validate: snapshot --validate ${snapshotId}`;
117
+ snapshot.tree = `${header}\n${snapshot.tree}\n${tips}`;
118
+ this.lastSnapshot = snapshot.tree;
119
+ return { ...snapshot, snapshotId };
120
+ }
121
+ /**
122
+ * Ensure selectors have been lazily generated for a snapshot.
123
+ * Generates them on first call, then caches in the store.
124
+ */
125
+ async ensureSelectorsGenerated(snapId) {
126
+ const store = this.snapshotStore;
127
+ if (store.isSelectorsGenerated(snapId))
128
+ return true;
129
+ const entry = store.get(snapId);
130
+ if (!entry)
131
+ return false;
132
+ const refs = {};
133
+ for (const [ref, el] of entry.elements) {
134
+ refs[ref] = {
135
+ selector: `getByRole('${el.role}'${el.name ? `, { name: "${el.name}", exact: true }` : ''})`,
136
+ role: el.role,
137
+ name: el.name,
138
+ };
139
+ }
140
+ const frame = entry.framePath ? this.getFrame(entry.framePath) : this.getFrame();
141
+ let stableSelectors = {};
142
+ try {
143
+ stableSelectors = await generateStableSelectors(frame, refs);
144
+ }
145
+ catch { }
146
+ for (const [ref, sel] of Object.entries(stableSelectors)) {
147
+ const el = entry.elements.get(ref);
148
+ if (el) {
149
+ el.cssSelector = sel.cssSelector;
150
+ el.xpath = sel.xpath;
151
+ }
152
+ }
153
+ store.markSelectorsGenerated(snapId);
154
+ return true;
97
155
  }
98
156
  /**
99
157
  * Get the cached ref map from last snapshot
@@ -101,6 +159,9 @@ export class BrowserManager {
101
159
  getRefMap() {
102
160
  return this.refMap;
103
161
  }
162
+ getSnapshotStore() {
163
+ return this.snapshotStore;
164
+ }
104
165
  recordCommand(action, selector, value, success) {
105
166
  this.commandHistory.push({ action, selector, value, success, timestamp: Date.now() });
106
167
  }
@@ -133,7 +194,10 @@ export class BrowserManager {
133
194
  }
134
195
  let locator;
135
196
  if (refData.name) {
136
- locator = frame.getByRole(refData.role, { name: refData.name, exact: true });
197
+ locator = frame.getByRole(refData.role, {
198
+ name: refData.name,
199
+ exact: true,
200
+ });
137
201
  }
138
202
  else {
139
203
  locator = frame.getByRole(refData.role);
@@ -2306,7 +2370,7 @@ export class BrowserManager {
2306
2370
  // 这个会第一个执行(后添加的先执行)
2307
2371
  // 注意:必须设置 xyzSessionId,否则 inject.js 会跳过初始化
2308
2372
  // 使用时间戳来确保只有最新的会话 ID 被设置
2309
- const sessionIdTimestamp = parseInt(this.recorderSessionId.replace('recorder-', '')) || Date.now();
2373
+ const sessionIdTimestamp = parseInt(this.recorderSessionId.replace('recorder-', ''), 10) || Date.now();
2310
2374
  await context.addInitScript({
2311
2375
  content: `
2312
2376
  // 只有当新的会话 ID 更新时才设置
@@ -2455,7 +2519,7 @@ export class BrowserManager {
2455
2519
  const pageIndex = this.getPageIndex(newPage);
2456
2520
  const newTabIndex = pageIndex >= 0 ? pageIndex : this.pages.length;
2457
2521
  this.recorderSteps.push({
2458
- id: this.recorderSteps.length + 1,
2522
+ id: `step-${this.recorderSteps.length + 1}`,
2459
2523
  timestamp: Date.now(),
2460
2524
  action: 'tab_new',
2461
2525
  url: newPage.url(),
@@ -2464,7 +2528,7 @@ export class BrowserManager {
2464
2528
  setTimeout(() => {
2465
2529
  if (this.recorderSessionId && this.activePageIndex !== previousActiveIndex) {
2466
2530
  this.recorderSteps.push({
2467
- id: this.recorderSteps.length + 1,
2531
+ id: `step-${this.recorderSteps.length + 1}`,
2468
2532
  timestamp: Date.now(),
2469
2533
  action: 'tab_switch',
2470
2534
  index: this.activePageIndex,
@@ -2475,7 +2539,7 @@ export class BrowserManager {
2475
2539
  if (this.recorderSessionId) {
2476
2540
  const closeIndex = this.getPageIndex(newPage);
2477
2541
  this.recorderSteps.push({
2478
- id: this.recorderSteps.length + 1,
2542
+ id: `step-${this.recorderSteps.length + 1}`,
2479
2543
  timestamp: Date.now(),
2480
2544
  action: 'tab_close',
2481
2545
  index: closeIndex >= 0 ? closeIndex : -1,
@@ -2531,8 +2595,6 @@ export class BrowserManager {
2531
2595
  const hasCloseFunc = typeof win.xyzClose === 'function';
2532
2596
  const hasFlushFunc = typeof win.xyzFlushPending === 'function';
2533
2597
  console.log('[stopRecorder] hasFlushFunc:', hasFlushFunc, 'hasCloseFunc:', hasCloseFunc, 'hasPanel:', hasPanel);
2534
- // 重要:先调用 xyzFlushPending,再设置 xyzStopped
2535
- // 因为 recordStep 会检查 xyzStopped,如果为 true 就不记录
2536
2598
  if (hasFlushFunc) {
2537
2599
  console.log('[stopRecorder] Calling xyzFlushPending');
2538
2600
  win.xyzFlushPending();
@@ -2540,15 +2602,10 @@ export class BrowserManager {
2540
2602
  else {
2541
2603
  console.log('[stopRecorder] xyzFlushPending not found');
2542
2604
  }
2543
- // 然后再设置停止标志
2544
2605
  win.xyzActive = false;
2545
2606
  win.xyzStopped = true;
2546
- // 重置初始化标志,允许新的录制会话重新初始化
2547
2607
  win.xyzInited = false;
2548
2608
  win.xyzInitializedSessionId = undefined;
2549
- // 注意:不要清除 xyzSessionId,因为旧的监听器需要用它来检查是否应该跳过
2550
- // 新的录制会话会设置新的 xyzSessionId,旧的监听器会检测到时间戳更新并跳过
2551
- // win.xyzSessionId = undefined;
2552
2609
  if (hasCloseFunc) {
2553
2610
  win.xyzClose();
2554
2611
  }
@@ -2665,13 +2722,13 @@ export class BrowserManager {
2665
2722
  if (step.from && typeof step.from === 'string') {
2666
2723
  lines.push(` from: "${step.from}"`);
2667
2724
  }
2668
- else if (step.from) {
2725
+ else if (step.from && typeof step.from === 'object') {
2669
2726
  lines.push(` from: { width: ${step.from.width}, height: ${step.from.height} }`);
2670
2727
  }
2671
2728
  if (step.to && typeof step.to === 'string') {
2672
2729
  lines.push(` to: "${step.to}"`);
2673
2730
  }
2674
- else if (step.to) {
2731
+ else if (step.to && typeof step.to === 'object') {
2675
2732
  lines.push(` to: { width: ${step.to.width}, height: ${step.to.height} }`);
2676
2733
  }
2677
2734
  // 备注信息 - 添加重点提示
@@ -2702,7 +2759,7 @@ export class BrowserManager {
2702
2759
  lines.push(` # User marked this as: "${step.annotation.label}"`);
2703
2760
  }
2704
2761
  // 只在特定操作类型时携带 URL
2705
- if (step.url && urlRequiredActions.includes(step.action)) {
2762
+ if (step.url && step.action && urlRequiredActions.includes(step.action)) {
2706
2763
  lines.push(` url: "${step.url}"`);
2707
2764
  }
2708
2765
  if (step.index !== undefined)
@@ -2758,6 +2815,7 @@ export class BrowserManager {
2758
2815
  if (s.altKey)
2759
2816
  parts.push('Alt');
2760
2817
  if (s.shiftKey &&
2818
+ s.key &&
2761
2819
  !['Shift', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(s.key)) {
2762
2820
  parts.push('Shift');
2763
2821
  }
@@ -2831,7 +2889,6 @@ export class BrowserManager {
2831
2889
  sampled = step.points;
2832
2890
  }
2833
2891
  else {
2834
- // 均匀采样
2835
2892
  sampled = [];
2836
2893
  const step_size = (step.points.length - 1) / (maxPoints - 1);
2837
2894
  for (let i = 0; i < maxPoints; i++) {
@@ -2839,7 +2896,6 @@ export class BrowserManager {
2839
2896
  sampled.push(step.points[idx]);
2840
2897
  }
2841
2898
  }
2842
- // 格式化为 x:y:delay 字符串
2843
2899
  const segments = sampled.map((p, i) => {
2844
2900
  const x = Math.round(p.x);
2845
2901
  const y = Math.round(p.y);